diff options
author | Ondřej Surý <ondrej@sury.org> | 2013-03-22 08:32:53 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2013-03-22 08:32:53 +0100 |
commit | f21eff8954d5956842795ea5653a9a5b8d62caa3 (patch) | |
tree | adbe3c3feb67f383afe32b3974794eb1d5ec8cc8 /ext | |
parent | 92984f18f6aee9c0f719febb9fc09a0c50262c2f (diff) | |
download | php-f21eff8954d5956842795ea5653a9a5b8d62caa3.tar.gz |
Imported Upstream version 5.5.0~beta1upstream/5.5.0_beta1
Diffstat (limited to 'ext')
125 files changed, 15020 insertions, 1090 deletions
diff --git a/ext/com_dotnet/com_iterator.c b/ext/com_dotnet/com_iterator.c index ce4bdd67c..ecf395b16 100644 --- a/ext/com_dotnet/com_iterator.c +++ b/ext/com_dotnet/com_iterator.c @@ -74,16 +74,15 @@ static void com_iter_get_data(zend_object_iterator *iter, zval ***data TSRMLS_DC *data = &I->zdata; } -static int com_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, - ulong *int_key TSRMLS_DC) +static void com_iter_get_key(zend_object_iterator *iter, zval *key TSRMLS_DC) { struct php_com_iterator *I = (struct php_com_iterator*)iter->data; if (I->key == (ulong)-1) { - return HASH_KEY_NON_EXISTANT; + ZVAL_NULL(key); + } else { + ZVAL_LONG(key, I->key); } - *int_key = I->key; - return HASH_KEY_IS_LONG; } static int com_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC) diff --git a/ext/com_dotnet/com_saproxy.c b/ext/com_dotnet/com_saproxy.c index ad9284974..5450370cd 100644 --- a/ext/com_dotnet/com_saproxy.c +++ b/ext/com_dotnet/com_saproxy.c @@ -519,16 +519,15 @@ static void saproxy_iter_get_data(zend_object_iterator *iter, zval ***data TSRML *data = ptr_ptr; } -static int saproxy_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, - ulong *int_key TSRMLS_DC) +static void saproxy_iter_get_key(zend_object_iterator *iter, zval *key TSRMLS_DC) { php_com_saproxy_iter *I = (php_com_saproxy_iter*)iter->data; if (I->key == -1) { - return HASH_KEY_NON_EXISTANT; + ZVAL_NULL(key); + } else { + ZVAL_LONG(key, I->key); } - *int_key = (ulong)I->key; - return HASH_KEY_IS_LONG; } static int saproxy_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC) diff --git a/ext/date/lib/timezonedb.h b/ext/date/lib/timezonedb.h index ba34c75b2..7bf9a9d22 100644 --- a/ext/date/lib/timezonedb.h +++ b/ext/date/lib/timezonedb.h @@ -14,573 +14,573 @@ const timelib_tzdb_index_entry timezonedb_idx_builtin[579] = { { "Africa/Bujumbura" , 0x000571 }, { "Africa/Cairo" , 0x0005B5 }, { "Africa/Casablanca" , 0x000878 }, - { "Africa/Ceuta" , 0x000A76 }, - { "Africa/Conakry" , 0x000D7D }, - { "Africa/Dakar" , 0x000DE8 }, - { "Africa/Dar_es_Salaam" , 0x000E4E }, - { "Africa/Djibouti" , 0x000EBB }, - { "Africa/Douala" , 0x000F10 }, - { "Africa/El_Aaiun" , 0x000F65 }, - { "Africa/Freetown" , 0x000FCB }, - { "Africa/Gaborone" , 0x0010DA }, - { "Africa/Harare" , 0x001147 }, - { "Africa/Johannesburg" , 0x00119C }, - { "Africa/Juba" , 0x00120A }, - { "Africa/Kampala" , 0x00131D }, - { "Africa/Khartoum" , 0x00139C }, - { "Africa/Kigali" , 0x0014AF }, - { "Africa/Kinshasa" , 0x001504 }, - { "Africa/Lagos" , 0x00155F }, - { "Africa/Libreville" , 0x0015B4 }, - { "Africa/Lome" , 0x001609 }, - { "Africa/Luanda" , 0x00164D }, - { "Africa/Lubumbashi" , 0x0016A2 }, - { "Africa/Lusaka" , 0x0016FD }, - { "Africa/Malabo" , 0x001752 }, - { "Africa/Maputo" , 0x0017B8 }, - { "Africa/Maseru" , 0x00180D }, - { "Africa/Mbabane" , 0x001875 }, - { "Africa/Mogadishu" , 0x0018CB }, - { "Africa/Monrovia" , 0x001926 }, - { "Africa/Nairobi" , 0x00198C }, - { "Africa/Ndjamena" , 0x001A0B }, - { "Africa/Niamey" , 0x001A77 }, - { "Africa/Nouakchott" , 0x001AEA }, - { "Africa/Ouagadougou" , 0x001B55 }, - { "Africa/Porto-Novo" , 0x001BAA }, - { "Africa/Sao_Tome" , 0x001C10 }, - { "Africa/Timbuktu" , 0x001C65 }, - { "Africa/Tripoli" , 0x001CD0 }, - { "Africa/Tunis" , 0x001EC9 }, - { "Africa/Windhoek" , 0x001FDB }, - { "America/Adak" , 0x002222 }, - { "America/Anchorage" , 0x002598 }, - { "America/Anguilla" , 0x00290C }, - { "America/Antigua" , 0x002961 }, - { "America/Araguaina" , 0x0029C7 }, - { "America/Argentina/Buenos_Aires" , 0x002C21 }, - { "America/Argentina/Catamarca" , 0x002DCF }, - { "America/Argentina/ComodRivadavia" , 0x002F90 }, - { "America/Argentina/Cordoba" , 0x003136 }, - { "America/Argentina/Jujuy" , 0x00330B }, - { "America/Argentina/La_Rioja" , 0x0034BF }, - { "America/Argentina/Mendoza" , 0x003677 }, - { "America/Argentina/Rio_Gallegos" , 0x003837 }, - { "America/Argentina/Salta" , 0x0039EC }, - { "America/Argentina/San_Juan" , 0x003B98 }, - { "America/Argentina/San_Luis" , 0x003D50 }, - { "America/Argentina/Tucuman" , 0x003F16 }, - { "America/Argentina/Ushuaia" , 0x0040D2 }, - { "America/Aruba" , 0x00428D }, - { "America/Asuncion" , 0x0042F3 }, - { "America/Atikokan" , 0x0045D8 }, - { "America/Atka" , 0x0046AE }, - { "America/Bahia" , 0x004A14 }, - { "America/Bahia_Banderas" , 0x004BA7 }, - { "America/Barbados" , 0x004E20 }, - { "America/Belem" , 0x004EBA }, - { "America/Belize" , 0x004FB5 }, - { "America/Blanc-Sablon" , 0x005131 }, - { "America/Boa_Vista" , 0x0051E5 }, - { "America/Bogota" , 0x0052EE }, - { "America/Boise" , 0x00535A }, - { "America/Buenos_Aires" , 0x0056F1 }, - { "America/Cambridge_Bay" , 0x00588A }, - { "America/Campo_Grande" , 0x005BB2 }, - { "America/Cancun" , 0x005EA1 }, - { "America/Caracas" , 0x0060E3 }, - { "America/Catamarca" , 0x00614A }, - { "America/Cayenne" , 0x0062F0 }, - { "America/Cayman" , 0x006352 }, - { "America/Chicago" , 0x0063A7 }, - { "America/Chihuahua" , 0x0068BE }, - { "America/Coral_Harbour" , 0x006B29 }, - { "America/Cordoba" , 0x006BBB }, - { "America/Costa_Rica" , 0x006D61 }, - { "America/Creston" , 0x006DEB }, - { "America/Cuiaba" , 0x006E77 }, - { "America/Curacao" , 0x007155 }, - { "America/Danmarkshavn" , 0x0071BB }, - { "America/Dawson" , 0x0072FF }, - { "America/Dawson_Creek" , 0x00761C }, - { "America/Denver" , 0x0077F6 }, - { "America/Detroit" , 0x007B7C }, - { "America/Dominica" , 0x007EDB }, - { "America/Edmonton" , 0x007F30 }, - { "America/Eirunepe" , 0x0082E8 }, - { "America/El_Salvador" , 0x0083FB }, - { "America/Ensenada" , 0x008470 }, - { "America/Fort_Wayne" , 0x008917 }, - { "America/Fortaleza" , 0x0087D9 }, - { "America/Glace_Bay" , 0x008B81 }, - { "America/Godthab" , 0x008EF8 }, - { "America/Goose_Bay" , 0x0091BC }, - { "America/Grand_Turk" , 0x009679 }, - { "America/Grenada" , 0x009928 }, - { "America/Guadeloupe" , 0x00997D }, - { "America/Guatemala" , 0x0099D2 }, - { "America/Guayaquil" , 0x009A5B }, - { "America/Guyana" , 0x009AB8 }, - { "America/Halifax" , 0x009B39 }, - { "America/Havana" , 0x00A04F }, - { "America/Hermosillo" , 0x00A3C2 }, - { "America/Indiana/Indianapolis" , 0x00A4A0 }, - { "America/Indiana/Knox" , 0x00A731 }, - { "America/Indiana/Marengo" , 0x00AAC8 }, - { "America/Indiana/Petersburg" , 0x00AD6E }, - { "America/Indiana/Tell_City" , 0x00B2BB }, - { "America/Indiana/Vevay" , 0x00B554 }, - { "America/Indiana/Vincennes" , 0x00B78F }, - { "America/Indiana/Winamac" , 0x00BA43 }, - { "America/Indianapolis" , 0x00B051 }, - { "America/Inuvik" , 0x00BCFC }, - { "America/Iqaluit" , 0x00BFF3 }, - { "America/Jamaica" , 0x00C315 }, - { "America/Jujuy" , 0x00C3DA }, - { "America/Juneau" , 0x00C584 }, - { "America/Kentucky/Louisville" , 0x00C902 }, - { "America/Kentucky/Monticello" , 0x00CD20 }, - { "America/Knox_IN" , 0x00D0A5 }, - { "America/Kralendijk" , 0x00D416 }, - { "America/La_Paz" , 0x00D47C }, - { "America/Lima" , 0x00D4E3 }, - { "America/Los_Angeles" , 0x00D58B }, - { "America/Louisville" , 0x00D99C }, - { "America/Lower_Princes" , 0x00DD91 }, - { "America/Maceio" , 0x00DDF7 }, - { "America/Managua" , 0x00DF31 }, - { "America/Manaus" , 0x00DFE4 }, - { "America/Marigot" , 0x00E0E6 }, - { "America/Martinique" , 0x00E13B }, - { "America/Matamoros" , 0x00E1A7 }, - { "America/Mazatlan" , 0x00E400 }, - { "America/Mendoza" , 0x00E66D }, - { "America/Menominee" , 0x00E821 }, - { "America/Merida" , 0x00EBA2 }, - { "America/Metlakatla" , 0x00EDDD }, - { "America/Mexico_City" , 0x00EF17 }, - { "America/Miquelon" , 0x00F192 }, - { "America/Moncton" , 0x00F404 }, - { "America/Monterrey" , 0x00F89B }, - { "America/Montevideo" , 0x00FAFE }, - { "America/Montreal" , 0x00FE10 }, - { "America/Montserrat" , 0x010326 }, - { "America/Nassau" , 0x01037B }, - { "America/New_York" , 0x0106C0 }, - { "America/Nipigon" , 0x010BCB }, - { "America/Nome" , 0x010F1C }, - { "America/Noronha" , 0x01129A }, - { "America/North_Dakota/Beulah" , 0x0113CA }, - { "America/North_Dakota/Center" , 0x01175E }, - { "America/North_Dakota/New_Salem" , 0x011AF2 }, - { "America/Ojinaga" , 0x011E9B }, - { "America/Panama" , 0x0120FC }, - { "America/Pangnirtung" , 0x012151 }, - { "America/Paramaribo" , 0x012487 }, - { "America/Phoenix" , 0x012519 }, - { "America/Port-au-Prince" , 0x0125C7 }, - { "America/Port_of_Spain" , 0x0127EC }, - { "America/Porto_Acre" , 0x0126ED }, - { "America/Porto_Velho" , 0x012841 }, - { "America/Puerto_Rico" , 0x012937 }, - { "America/Rainy_River" , 0x0129A2 }, - { "America/Rankin_Inlet" , 0x012CDA }, - { "America/Recife" , 0x012FC0 }, - { "America/Regina" , 0x0130EA }, - { "America/Resolute" , 0x0132A8 }, - { "America/Rio_Branco" , 0x013599 }, - { "America/Rosario" , 0x01369C }, - { "America/Santa_Isabel" , 0x013842 }, - { "America/Santarem" , 0x013BE5 }, - { "America/Santiago" , 0x013CEA }, - { "America/Santo_Domingo" , 0x014093 }, - { "America/Sao_Paulo" , 0x014159 }, - { "America/Scoresbysund" , 0x014468 }, - { "America/Shiprock" , 0x014756 }, - { "America/Sitka" , 0x014AE5 }, - { "America/St_Barthelemy" , 0x014E6D }, - { "America/St_Johns" , 0x014EC2 }, - { "America/St_Kitts" , 0x015415 }, - { "America/St_Lucia" , 0x01546A }, - { "America/St_Thomas" , 0x0154BF }, - { "America/St_Vincent" , 0x015514 }, - { "America/Swift_Current" , 0x015569 }, - { "America/Tegucigalpa" , 0x01568A }, - { "America/Thule" , 0x015709 }, - { "America/Thunder_Bay" , 0x015950 }, - { "America/Tijuana" , 0x015C99 }, - { "America/Toronto" , 0x016032 }, - { "America/Tortola" , 0x016549 }, - { "America/Vancouver" , 0x01659E }, - { "America/Virgin" , 0x0169DB }, - { "America/Whitehorse" , 0x016A30 }, - { "America/Winnipeg" , 0x016D4D }, - { "America/Yakutat" , 0x01718D }, - { "America/Yellowknife" , 0x0174F8 }, - { "Antarctica/Casey" , 0x017808 }, - { "Antarctica/Davis" , 0x0178A5 }, - { "Antarctica/DumontDUrville" , 0x017946 }, - { "Antarctica/Macquarie" , 0x0179D8 }, - { "Antarctica/Mawson" , 0x017C52 }, - { "Antarctica/McMurdo" , 0x017CCE }, - { "Antarctica/Palmer" , 0x017FD0 }, - { "Antarctica/Rothera" , 0x0182EC }, - { "Antarctica/South_Pole" , 0x018362 }, - { "Antarctica/Syowa" , 0x01866A }, - { "Antarctica/Vostok" , 0x0186D8 }, - { "Arctic/Longyearbyen" , 0x018749 }, - { "Asia/Aden" , 0x018A7B }, - { "Asia/Almaty" , 0x018AD0 }, - { "Asia/Amman" , 0x018C4F }, - { "Asia/Anadyr" , 0x018F05 }, - { "Asia/Aqtau" , 0x0190EA }, - { "Asia/Aqtobe" , 0x0192E9 }, - { "Asia/Ashgabat" , 0x0194A1 }, - { "Asia/Ashkhabad" , 0x0195BE }, - { "Asia/Baghdad" , 0x0196DB }, - { "Asia/Bahrain" , 0x019850 }, - { "Asia/Baku" , 0x0198B6 }, - { "Asia/Bangkok" , 0x019B9E }, - { "Asia/Beirut" , 0x019BF3 }, - { "Asia/Bishkek" , 0x019F00 }, - { "Asia/Brunei" , 0x01A0AC }, - { "Asia/Calcutta" , 0x01A10E }, - { "Asia/Choibalsan" , 0x01A187 }, - { "Asia/Chongqing" , 0x01A300 }, - { "Asia/Chungking" , 0x01A3EF }, - { "Asia/Colombo" , 0x01A49E }, - { "Asia/Dacca" , 0x01A53A }, - { "Asia/Damascus" , 0x01A5E0 }, - { "Asia/Dhaka" , 0x01A930 }, - { "Asia/Dili" , 0x01A9D6 }, - { "Asia/Dubai" , 0x01AA5F }, - { "Asia/Dushanbe" , 0x01AAB4 }, - { "Asia/Gaza" , 0x01ABB7 }, - { "Asia/Harbin" , 0x01AE10 }, - { "Asia/Hebron" , 0x01AEF7 }, - { "Asia/Ho_Chi_Minh" , 0x01B159 }, - { "Asia/Hong_Kong" , 0x01B1D1 }, - { "Asia/Hovd" , 0x01B393 }, - { "Asia/Irkutsk" , 0x01B50B }, - { "Asia/Istanbul" , 0x01B6F1 }, - { "Asia/Jakarta" , 0x01BADE }, - { "Asia/Jayapura" , 0x01BB88 }, - { "Asia/Jerusalem" , 0x01BC24 }, - { "Asia/Kabul" , 0x01BF53 }, - { "Asia/Kamchatka" , 0x01BFA4 }, - { "Asia/Karachi" , 0x01C180 }, - { "Asia/Kashgar" , 0x01C235 }, - { "Asia/Kathmandu" , 0x01C306 }, - { "Asia/Katmandu" , 0x01C36C }, - { "Asia/Khandyga" , 0x01C3D2 }, - { "Asia/Kolkata" , 0x01C5F7 }, - { "Asia/Krasnoyarsk" , 0x01C670 }, - { "Asia/Kuala_Lumpur" , 0x01C858 }, - { "Asia/Kuching" , 0x01C915 }, - { "Asia/Kuwait" , 0x01CA03 }, - { "Asia/Macao" , 0x01CA58 }, - { "Asia/Macau" , 0x01CB93 }, - { "Asia/Magadan" , 0x01CCCE }, - { "Asia/Makassar" , 0x01CEB0 }, - { "Asia/Manila" , 0x01CF74 }, - { "Asia/Muscat" , 0x01CFF9 }, - { "Asia/Nicosia" , 0x01D04E }, - { "Asia/Novokuznetsk" , 0x01D336 }, - { "Asia/Novosibirsk" , 0x01D538 }, - { "Asia/Omsk" , 0x01D723 }, - { "Asia/Oral" , 0x01D90A }, - { "Asia/Phnom_Penh" , 0x01DADA }, - { "Asia/Pontianak" , 0x01DB52 }, - { "Asia/Pyongyang" , 0x01DC13 }, - { "Asia/Qatar" , 0x01DC80 }, - { "Asia/Qyzylorda" , 0x01DCE6 }, - { "Asia/Rangoon" , 0x01DEBC }, - { "Asia/Riyadh" , 0x01DF34 }, - { "Asia/Saigon" , 0x01DF89 }, - { "Asia/Sakhalin" , 0x01E001 }, - { "Asia/Samarkand" , 0x01E1F8 }, - { "Asia/Seoul" , 0x01E32E }, - { "Asia/Shanghai" , 0x01E3D2 }, - { "Asia/Singapore" , 0x01E4B2 }, - { "Asia/Taipei" , 0x01E569 }, - { "Asia/Tashkent" , 0x01E681 }, - { "Asia/Tbilisi" , 0x01E7B2 }, - { "Asia/Tehran" , 0x01E96C }, - { "Asia/Tel_Aviv" , 0x01EBDA }, - { "Asia/Thimbu" , 0x01EF09 }, - { "Asia/Thimphu" , 0x01EF6F }, - { "Asia/Tokyo" , 0x01EFD5 }, - { "Asia/Ujung_Pandang" , 0x01F05E }, - { "Asia/Ulaanbaatar" , 0x01F0DA }, - { "Asia/Ulan_Bator" , 0x01F235 }, - { "Asia/Urumqi" , 0x01F382 }, - { "Asia/Ust-Nera" , 0x01F449 }, - { "Asia/Vientiane" , 0x01F64E }, - { "Asia/Vladivostok" , 0x01F6C6 }, - { "Asia/Yakutsk" , 0x01F8B2 }, - { "Asia/Yekaterinburg" , 0x01FA97 }, - { "Asia/Yerevan" , 0x01FCA2 }, - { "Atlantic/Azores" , 0x01FEA2 }, - { "Atlantic/Bermuda" , 0x0203A5 }, - { "Atlantic/Canary" , 0x020686 }, - { "Atlantic/Cape_Verde" , 0x02095C }, - { "Atlantic/Faeroe" , 0x0209D5 }, - { "Atlantic/Faroe" , 0x020C79 }, - { "Atlantic/Jan_Mayen" , 0x020F1D }, - { "Atlantic/Madeira" , 0x02124F }, - { "Atlantic/Reykjavik" , 0x021758 }, - { "Atlantic/South_Georgia" , 0x021911 }, - { "Atlantic/St_Helena" , 0x021B23 }, - { "Atlantic/Stanley" , 0x021955 }, - { "Australia/ACT" , 0x021B78 }, - { "Australia/Adelaide" , 0x021E95 }, - { "Australia/Brisbane" , 0x0221C1 }, - { "Australia/Broken_Hill" , 0x022288 }, - { "Australia/Canberra" , 0x0225C6 }, - { "Australia/Currie" , 0x0228E3 }, - { "Australia/Darwin" , 0x022C16 }, - { "Australia/Eucla" , 0x022C9C }, - { "Australia/Hobart" , 0x022D71 }, - { "Australia/LHI" , 0x0230CF }, - { "Australia/Lindeman" , 0x02336A }, - { "Australia/Lord_Howe" , 0x02344B }, - { "Australia/Melbourne" , 0x0236F6 }, - { "Australia/North" , 0x023A1B }, - { "Australia/NSW" , 0x023A8F }, - { "Australia/Perth" , 0x023DAC }, - { "Australia/Queensland" , 0x023E84 }, - { "Australia/South" , 0x023F30 }, - { "Australia/Sydney" , 0x02424D }, - { "Australia/Tasmania" , 0x02458A }, - { "Australia/Victoria" , 0x0248CF }, - { "Australia/West" , 0x024BEC }, - { "Australia/Yancowinna" , 0x024CA2 }, - { "Brazil/Acre" , 0x024FC4 }, - { "Brazil/DeNoronha" , 0x0250C3 }, - { "Brazil/East" , 0x0251E3 }, - { "Brazil/West" , 0x0254C0 }, - { "Canada/Atlantic" , 0x0255B8 }, - { "Canada/Central" , 0x025AA0 }, - { "Canada/East-Saskatchewan" , 0x0263AA }, - { "Canada/Eastern" , 0x025EBA }, - { "Canada/Mountain" , 0x026533 }, - { "Canada/Newfoundland" , 0x0268A9 }, - { "Canada/Pacific" , 0x026DD4 }, - { "Canada/Saskatchewan" , 0x0271ED }, - { "Canada/Yukon" , 0x027376 }, - { "CET" , 0x027679 }, - { "Chile/Continental" , 0x027982 }, - { "Chile/EasterIsland" , 0x027D1D }, - { "CST6CDT" , 0x02805F }, - { "Cuba" , 0x0283B0 }, - { "EET" , 0x028723 }, - { "Egypt" , 0x0289D6 }, - { "Eire" , 0x028C99 }, - { "EST" , 0x0291AA }, - { "EST5EDT" , 0x0291EE }, - { "Etc/GMT" , 0x02953F }, - { "Etc/GMT+0" , 0x02960B }, - { "Etc/GMT+1" , 0x029695 }, - { "Etc/GMT+10" , 0x029722 }, - { "Etc/GMT+11" , 0x0297B0 }, - { "Etc/GMT+12" , 0x02983E }, - { "Etc/GMT+2" , 0x029959 }, - { "Etc/GMT+3" , 0x0299E5 }, - { "Etc/GMT+4" , 0x029A71 }, - { "Etc/GMT+5" , 0x029AFD }, - { "Etc/GMT+6" , 0x029B89 }, - { "Etc/GMT+7" , 0x029C15 }, - { "Etc/GMT+8" , 0x029CA1 }, - { "Etc/GMT+9" , 0x029D2D }, - { "Etc/GMT-0" , 0x0295C7 }, - { "Etc/GMT-1" , 0x02964F }, - { "Etc/GMT-10" , 0x0296DB }, - { "Etc/GMT-11" , 0x029769 }, - { "Etc/GMT-12" , 0x0297F7 }, - { "Etc/GMT-13" , 0x029885 }, - { "Etc/GMT-14" , 0x0298CC }, - { "Etc/GMT-2" , 0x029913 }, - { "Etc/GMT-3" , 0x02999F }, - { "Etc/GMT-4" , 0x029A2B }, - { "Etc/GMT-5" , 0x029AB7 }, - { "Etc/GMT-6" , 0x029B43 }, - { "Etc/GMT-7" , 0x029BCF }, - { "Etc/GMT-8" , 0x029C5B }, - { "Etc/GMT-9" , 0x029CE7 }, - { "Etc/GMT0" , 0x029583 }, - { "Etc/Greenwich" , 0x029D73 }, - { "Etc/UCT" , 0x029DB7 }, - { "Etc/Universal" , 0x029DFB }, - { "Etc/UTC" , 0x029E3F }, - { "Etc/Zulu" , 0x029E83 }, - { "Europe/Amsterdam" , 0x029EC7 }, - { "Europe/Andorra" , 0x02A305 }, - { "Europe/Athens" , 0x02A581 }, - { "Europe/Belfast" , 0x02A8C4 }, - { "Europe/Belgrade" , 0x02ADFB }, - { "Europe/Berlin" , 0x02B0C4 }, - { "Europe/Bratislava" , 0x02B428 }, - { "Europe/Brussels" , 0x02B75A }, - { "Europe/Bucharest" , 0x02BB91 }, - { "Europe/Budapest" , 0x02BEBB }, - { "Europe/Busingen" , 0x02C22E }, - { "Europe/Chisinau" , 0x02C4E5 }, - { "Europe/Copenhagen" , 0x02C873 }, - { "Europe/Dublin" , 0x02CB7D }, - { "Europe/Gibraltar" , 0x02D08E }, - { "Europe/Guernsey" , 0x02D4E5 }, - { "Europe/Helsinki" , 0x02DA1C }, - { "Europe/Isle_of_Man" , 0x02DCD2 }, - { "Europe/Istanbul" , 0x02E209 }, - { "Europe/Jersey" , 0x02E5F6 }, - { "Europe/Kaliningrad" , 0x02EB2D }, - { "Europe/Kiev" , 0x02ED93 }, - { "Europe/Lisbon" , 0x02F0AA }, - { "Europe/Ljubljana" , 0x02F5AE }, - { "Europe/London" , 0x02F877 }, - { "Europe/Luxembourg" , 0x02FDAE }, - { "Europe/Madrid" , 0x030204 }, - { "Europe/Malta" , 0x0305CA }, - { "Europe/Mariehamn" , 0x030983 }, - { "Europe/Minsk" , 0x030C39 }, - { "Europe/Monaco" , 0x030E47 }, - { "Europe/Moscow" , 0x031282 }, - { "Europe/Nicosia" , 0x0314D3 }, - { "Europe/Oslo" , 0x0317BB }, - { "Europe/Paris" , 0x031AED }, - { "Europe/Podgorica" , 0x031F33 }, - { "Europe/Prague" , 0x0321FC }, - { "Europe/Riga" , 0x03252E }, - { "Europe/Rome" , 0x032873 }, - { "Europe/Samara" , 0x032C36 }, - { "Europe/San_Marino" , 0x032E69 }, - { "Europe/Sarajevo" , 0x03322C }, - { "Europe/Simferopol" , 0x0334F5 }, - { "Europe/Skopje" , 0x033820 }, - { "Europe/Sofia" , 0x033AE9 }, - { "Europe/Stockholm" , 0x033DF1 }, - { "Europe/Tallinn" , 0x0340A0 }, - { "Europe/Tirane" , 0x0343DA }, - { "Europe/Tiraspol" , 0x0346E0 }, - { "Europe/Uzhgorod" , 0x034A6E }, - { "Europe/Vaduz" , 0x034D85 }, - { "Europe/Vatican" , 0x035018 }, - { "Europe/Vienna" , 0x0353DB }, - { "Europe/Vilnius" , 0x035708 }, - { "Europe/Volgograd" , 0x035A47 }, - { "Europe/Warsaw" , 0x035C47 }, - { "Europe/Zagreb" , 0x036028 }, - { "Europe/Zaporozhye" , 0x0362F1 }, - { "Europe/Zurich" , 0x036632 }, - { "Factory" , 0x0368E1 }, - { "GB" , 0x036952 }, - { "GB-Eire" , 0x036E89 }, - { "GMT" , 0x0373C0 }, - { "GMT+0" , 0x03748C }, - { "GMT-0" , 0x037448 }, - { "GMT0" , 0x037404 }, - { "Greenwich" , 0x0374D0 }, - { "Hongkong" , 0x037514 }, - { "HST" , 0x0376D6 }, - { "Iceland" , 0x03771A }, - { "Indian/Antananarivo" , 0x0378D3 }, - { "Indian/Chagos" , 0x037947 }, - { "Indian/Christmas" , 0x0379A9 }, - { "Indian/Cocos" , 0x0379ED }, - { "Indian/Comoro" , 0x037A31 }, - { "Indian/Kerguelen" , 0x037A86 }, - { "Indian/Mahe" , 0x037ADB }, - { "Indian/Maldives" , 0x037B30 }, - { "Indian/Mauritius" , 0x037B85 }, - { "Indian/Mayotte" , 0x037BFB }, - { "Indian/Reunion" , 0x037C50 }, - { "Iran" , 0x037CA5 }, - { "Israel" , 0x037F13 }, - { "Jamaica" , 0x038242 }, - { "Japan" , 0x038307 }, - { "Kwajalein" , 0x038390 }, - { "Libya" , 0x0383F3 }, - { "MET" , 0x0385EC }, - { "Mexico/BajaNorte" , 0x0388F5 }, - { "Mexico/BajaSur" , 0x038C5E }, - { "Mexico/General" , 0x038EA3 }, - { "MST" , 0x039101 }, - { "MST7MDT" , 0x039145 }, - { "Navajo" , 0x039496 }, - { "NZ" , 0x03980F }, - { "NZ-CHAT" , 0x039B8D }, - { "Pacific/Apia" , 0x039E75 }, - { "Pacific/Auckland" , 0x03A011 }, - { "Pacific/Chatham" , 0x03A39D }, - { "Pacific/Chuuk" , 0x03A694 }, - { "Pacific/Easter" , 0x03A6ED }, - { "Pacific/Efate" , 0x03AA4B }, - { "Pacific/Enderbury" , 0x03AB11 }, - { "Pacific/Fakaofo" , 0x03AB7F }, - { "Pacific/Fiji" , 0x03ABD0 }, - { "Pacific/Funafuti" , 0x03AD63 }, - { "Pacific/Galapagos" , 0x03ADA7 }, - { "Pacific/Gambier" , 0x03AE1F }, - { "Pacific/Guadalcanal" , 0x03AE84 }, - { "Pacific/Guam" , 0x03AED9 }, - { "Pacific/Honolulu" , 0x03AF2F }, - { "Pacific/Johnston" , 0x03AFA6 }, - { "Pacific/Kiritimati" , 0x03AFF8 }, - { "Pacific/Kosrae" , 0x03B063 }, - { "Pacific/Kwajalein" , 0x03B0C0 }, - { "Pacific/Majuro" , 0x03B12C }, - { "Pacific/Marquesas" , 0x03B18B }, - { "Pacific/Midway" , 0x03B1F2 }, - { "Pacific/Nauru" , 0x03B27C }, - { "Pacific/Niue" , 0x03B2F4 }, - { "Pacific/Norfolk" , 0x03B352 }, - { "Pacific/Noumea" , 0x03B3A7 }, - { "Pacific/Pago_Pago" , 0x03B437 }, - { "Pacific/Palau" , 0x03B4C0 }, - { "Pacific/Pitcairn" , 0x03B504 }, - { "Pacific/Pohnpei" , 0x03B559 }, - { "Pacific/Ponape" , 0x03B5AE }, - { "Pacific/Port_Moresby" , 0x03B5F3 }, - { "Pacific/Rarotonga" , 0x03B637 }, - { "Pacific/Saipan" , 0x03B713 }, - { "Pacific/Samoa" , 0x03B776 }, - { "Pacific/Tahiti" , 0x03B7FF }, - { "Pacific/Tarawa" , 0x03B864 }, - { "Pacific/Tongatapu" , 0x03B8B8 }, - { "Pacific/Truk" , 0x03B944 }, - { "Pacific/Wake" , 0x03B989 }, - { "Pacific/Wallis" , 0x03B9D9 }, - { "Pacific/Yap" , 0x03BA1D }, - { "Poland" , 0x03BA62 }, - { "Portugal" , 0x03BE43 }, - { "PRC" , 0x03C33F }, - { "PST8PDT" , 0x03C3F0 }, - { "ROC" , 0x03C741 }, - { "ROK" , 0x03C859 }, - { "Singapore" , 0x03C8FD }, - { "Turkey" , 0x03C9B4 }, - { "UCT" , 0x03CDA1 }, - { "Universal" , 0x03CDE5 }, - { "US/Alaska" , 0x03CE29 }, - { "US/Aleutian" , 0x03D192 }, - { "US/Arizona" , 0x03D4F8 }, - { "US/Central" , 0x03D586 }, - { "US/East-Indiana" , 0x03DF90 }, - { "US/Eastern" , 0x03DA91 }, - { "US/Hawaii" , 0x03E1FA }, - { "US/Indiana-Starke" , 0x03E26B }, - { "US/Michigan" , 0x03E5DC }, - { "US/Mountain" , 0x03E913 }, - { "US/Pacific" , 0x03EC8C }, - { "US/Pacific-New" , 0x03F091 }, - { "US/Samoa" , 0x03F496 }, - { "UTC" , 0x03F51F }, - { "W-SU" , 0x03F816 }, - { "WET" , 0x03F563 }, - { "Zulu" , 0x03FA50 }, + { "Africa/Ceuta" , 0x000ABC }, + { "Africa/Conakry" , 0x000DC3 }, + { "Africa/Dakar" , 0x000E2E }, + { "Africa/Dar_es_Salaam" , 0x000E94 }, + { "Africa/Djibouti" , 0x000F01 }, + { "Africa/Douala" , 0x000F56 }, + { "Africa/El_Aaiun" , 0x000FAB }, + { "Africa/Freetown" , 0x001011 }, + { "Africa/Gaborone" , 0x001120 }, + { "Africa/Harare" , 0x00118D }, + { "Africa/Johannesburg" , 0x0011E2 }, + { "Africa/Juba" , 0x001250 }, + { "Africa/Kampala" , 0x001363 }, + { "Africa/Khartoum" , 0x0013E2 }, + { "Africa/Kigali" , 0x0014F5 }, + { "Africa/Kinshasa" , 0x00154A }, + { "Africa/Lagos" , 0x0015A5 }, + { "Africa/Libreville" , 0x0015FA }, + { "Africa/Lome" , 0x00164F }, + { "Africa/Luanda" , 0x001693 }, + { "Africa/Lubumbashi" , 0x0016E8 }, + { "Africa/Lusaka" , 0x001743 }, + { "Africa/Malabo" , 0x001798 }, + { "Africa/Maputo" , 0x0017FE }, + { "Africa/Maseru" , 0x001853 }, + { "Africa/Mbabane" , 0x0018BB }, + { "Africa/Mogadishu" , 0x001911 }, + { "Africa/Monrovia" , 0x00196C }, + { "Africa/Nairobi" , 0x0019D2 }, + { "Africa/Ndjamena" , 0x001A51 }, + { "Africa/Niamey" , 0x001ABD }, + { "Africa/Nouakchott" , 0x001B30 }, + { "Africa/Ouagadougou" , 0x001B9B }, + { "Africa/Porto-Novo" , 0x001BF0 }, + { "Africa/Sao_Tome" , 0x001C56 }, + { "Africa/Timbuktu" , 0x001CAB }, + { "Africa/Tripoli" , 0x001D16 }, + { "Africa/Tunis" , 0x001F0F }, + { "Africa/Windhoek" , 0x002021 }, + { "America/Adak" , 0x002268 }, + { "America/Anchorage" , 0x0025DE }, + { "America/Anguilla" , 0x002952 }, + { "America/Antigua" , 0x0029A7 }, + { "America/Araguaina" , 0x002A0D }, + { "America/Argentina/Buenos_Aires" , 0x002C67 }, + { "America/Argentina/Catamarca" , 0x002E15 }, + { "America/Argentina/ComodRivadavia" , 0x002FD6 }, + { "America/Argentina/Cordoba" , 0x00317C }, + { "America/Argentina/Jujuy" , 0x003351 }, + { "America/Argentina/La_Rioja" , 0x003505 }, + { "America/Argentina/Mendoza" , 0x0036BD }, + { "America/Argentina/Rio_Gallegos" , 0x00387D }, + { "America/Argentina/Salta" , 0x003A32 }, + { "America/Argentina/San_Juan" , 0x003BDE }, + { "America/Argentina/San_Luis" , 0x003D96 }, + { "America/Argentina/Tucuman" , 0x003F5C }, + { "America/Argentina/Ushuaia" , 0x004118 }, + { "America/Aruba" , 0x0042D3 }, + { "America/Asuncion" , 0x004339 }, + { "America/Atikokan" , 0x00461E }, + { "America/Atka" , 0x0046F4 }, + { "America/Bahia" , 0x004A5A }, + { "America/Bahia_Banderas" , 0x004BED }, + { "America/Barbados" , 0x004E66 }, + { "America/Belem" , 0x004F00 }, + { "America/Belize" , 0x004FFB }, + { "America/Blanc-Sablon" , 0x005177 }, + { "America/Boa_Vista" , 0x00522B }, + { "America/Bogota" , 0x005334 }, + { "America/Boise" , 0x0053A0 }, + { "America/Buenos_Aires" , 0x005737 }, + { "America/Cambridge_Bay" , 0x0058D0 }, + { "America/Campo_Grande" , 0x005BF8 }, + { "America/Cancun" , 0x005EE7 }, + { "America/Caracas" , 0x006129 }, + { "America/Catamarca" , 0x006190 }, + { "America/Cayenne" , 0x006336 }, + { "America/Cayman" , 0x006398 }, + { "America/Chicago" , 0x0063ED }, + { "America/Chihuahua" , 0x006904 }, + { "America/Coral_Harbour" , 0x006B6F }, + { "America/Cordoba" , 0x006C01 }, + { "America/Costa_Rica" , 0x006DA7 }, + { "America/Creston" , 0x006E31 }, + { "America/Cuiaba" , 0x006EBD }, + { "America/Curacao" , 0x00719B }, + { "America/Danmarkshavn" , 0x007201 }, + { "America/Dawson" , 0x007345 }, + { "America/Dawson_Creek" , 0x007662 }, + { "America/Denver" , 0x00783C }, + { "America/Detroit" , 0x007BC2 }, + { "America/Dominica" , 0x007F21 }, + { "America/Edmonton" , 0x007F76 }, + { "America/Eirunepe" , 0x00832E }, + { "America/El_Salvador" , 0x008441 }, + { "America/Ensenada" , 0x0084B6 }, + { "America/Fort_Wayne" , 0x00895D }, + { "America/Fortaleza" , 0x00881F }, + { "America/Glace_Bay" , 0x008BC7 }, + { "America/Godthab" , 0x008F3E }, + { "America/Goose_Bay" , 0x009202 }, + { "America/Grand_Turk" , 0x0096BF }, + { "America/Grenada" , 0x00996E }, + { "America/Guadeloupe" , 0x0099C3 }, + { "America/Guatemala" , 0x009A18 }, + { "America/Guayaquil" , 0x009AA1 }, + { "America/Guyana" , 0x009AFE }, + { "America/Halifax" , 0x009B7F }, + { "America/Havana" , 0x00A095 }, + { "America/Hermosillo" , 0x00A408 }, + { "America/Indiana/Indianapolis" , 0x00A4E6 }, + { "America/Indiana/Knox" , 0x00A777 }, + { "America/Indiana/Marengo" , 0x00AB0E }, + { "America/Indiana/Petersburg" , 0x00ADB4 }, + { "America/Indiana/Tell_City" , 0x00B301 }, + { "America/Indiana/Vevay" , 0x00B59A }, + { "America/Indiana/Vincennes" , 0x00B7D5 }, + { "America/Indiana/Winamac" , 0x00BA89 }, + { "America/Indianapolis" , 0x00B097 }, + { "America/Inuvik" , 0x00BD42 }, + { "America/Iqaluit" , 0x00C039 }, + { "America/Jamaica" , 0x00C35B }, + { "America/Jujuy" , 0x00C420 }, + { "America/Juneau" , 0x00C5CA }, + { "America/Kentucky/Louisville" , 0x00C948 }, + { "America/Kentucky/Monticello" , 0x00CD66 }, + { "America/Knox_IN" , 0x00D0EB }, + { "America/Kralendijk" , 0x00D45C }, + { "America/La_Paz" , 0x00D4C2 }, + { "America/Lima" , 0x00D529 }, + { "America/Los_Angeles" , 0x00D5D1 }, + { "America/Louisville" , 0x00D9E2 }, + { "America/Lower_Princes" , 0x00DDD7 }, + { "America/Maceio" , 0x00DE3D }, + { "America/Managua" , 0x00DF77 }, + { "America/Manaus" , 0x00E02A }, + { "America/Marigot" , 0x00E12C }, + { "America/Martinique" , 0x00E181 }, + { "America/Matamoros" , 0x00E1ED }, + { "America/Mazatlan" , 0x00E446 }, + { "America/Mendoza" , 0x00E6B3 }, + { "America/Menominee" , 0x00E867 }, + { "America/Merida" , 0x00EBE8 }, + { "America/Metlakatla" , 0x00EE23 }, + { "America/Mexico_City" , 0x00EF5D }, + { "America/Miquelon" , 0x00F1D8 }, + { "America/Moncton" , 0x00F44A }, + { "America/Monterrey" , 0x00F8E1 }, + { "America/Montevideo" , 0x00FB44 }, + { "America/Montreal" , 0x00FE56 }, + { "America/Montserrat" , 0x01036C }, + { "America/Nassau" , 0x0103C1 }, + { "America/New_York" , 0x010706 }, + { "America/Nipigon" , 0x010C11 }, + { "America/Nome" , 0x010F62 }, + { "America/Noronha" , 0x0112E0 }, + { "America/North_Dakota/Beulah" , 0x011410 }, + { "America/North_Dakota/Center" , 0x0117A4 }, + { "America/North_Dakota/New_Salem" , 0x011B38 }, + { "America/Ojinaga" , 0x011EE1 }, + { "America/Panama" , 0x012142 }, + { "America/Pangnirtung" , 0x012197 }, + { "America/Paramaribo" , 0x0124CD }, + { "America/Phoenix" , 0x01255F }, + { "America/Port-au-Prince" , 0x01260D }, + { "America/Port_of_Spain" , 0x01292C }, + { "America/Porto_Acre" , 0x01282D }, + { "America/Porto_Velho" , 0x012981 }, + { "America/Puerto_Rico" , 0x012A77 }, + { "America/Rainy_River" , 0x012AE2 }, + { "America/Rankin_Inlet" , 0x012E1A }, + { "America/Recife" , 0x013100 }, + { "America/Regina" , 0x01322A }, + { "America/Resolute" , 0x0133E8 }, + { "America/Rio_Branco" , 0x0136D9 }, + { "America/Rosario" , 0x0137DC }, + { "America/Santa_Isabel" , 0x013982 }, + { "America/Santarem" , 0x013D25 }, + { "America/Santiago" , 0x013E2A }, + { "America/Santo_Domingo" , 0x0141D3 }, + { "America/Sao_Paulo" , 0x014299 }, + { "America/Scoresbysund" , 0x0145A8 }, + { "America/Shiprock" , 0x014896 }, + { "America/Sitka" , 0x014C25 }, + { "America/St_Barthelemy" , 0x014FAD }, + { "America/St_Johns" , 0x015002 }, + { "America/St_Kitts" , 0x015555 }, + { "America/St_Lucia" , 0x0155AA }, + { "America/St_Thomas" , 0x0155FF }, + { "America/St_Vincent" , 0x015654 }, + { "America/Swift_Current" , 0x0156A9 }, + { "America/Tegucigalpa" , 0x0157CA }, + { "America/Thule" , 0x015849 }, + { "America/Thunder_Bay" , 0x015A90 }, + { "America/Tijuana" , 0x015DD9 }, + { "America/Toronto" , 0x016172 }, + { "America/Tortola" , 0x016689 }, + { "America/Vancouver" , 0x0166DE }, + { "America/Virgin" , 0x016B1B }, + { "America/Whitehorse" , 0x016B70 }, + { "America/Winnipeg" , 0x016E8D }, + { "America/Yakutat" , 0x0172CD }, + { "America/Yellowknife" , 0x017638 }, + { "Antarctica/Casey" , 0x017948 }, + { "Antarctica/Davis" , 0x0179E5 }, + { "Antarctica/DumontDUrville" , 0x017A86 }, + { "Antarctica/Macquarie" , 0x017B18 }, + { "Antarctica/Mawson" , 0x017D92 }, + { "Antarctica/McMurdo" , 0x017E0E }, + { "Antarctica/Palmer" , 0x018110 }, + { "Antarctica/Rothera" , 0x01842C }, + { "Antarctica/South_Pole" , 0x0184A2 }, + { "Antarctica/Syowa" , 0x0187AA }, + { "Antarctica/Vostok" , 0x018818 }, + { "Arctic/Longyearbyen" , 0x018889 }, + { "Asia/Aden" , 0x018BBB }, + { "Asia/Almaty" , 0x018C10 }, + { "Asia/Amman" , 0x018D8F }, + { "Asia/Anadyr" , 0x019045 }, + { "Asia/Aqtau" , 0x01922A }, + { "Asia/Aqtobe" , 0x019429 }, + { "Asia/Ashgabat" , 0x0195E1 }, + { "Asia/Ashkhabad" , 0x0196FE }, + { "Asia/Baghdad" , 0x01981B }, + { "Asia/Bahrain" , 0x019990 }, + { "Asia/Baku" , 0x0199F6 }, + { "Asia/Bangkok" , 0x019CDE }, + { "Asia/Beirut" , 0x019D33 }, + { "Asia/Bishkek" , 0x01A040 }, + { "Asia/Brunei" , 0x01A1EC }, + { "Asia/Calcutta" , 0x01A24E }, + { "Asia/Choibalsan" , 0x01A2C7 }, + { "Asia/Chongqing" , 0x01A440 }, + { "Asia/Chungking" , 0x01A52F }, + { "Asia/Colombo" , 0x01A5DE }, + { "Asia/Dacca" , 0x01A67A }, + { "Asia/Damascus" , 0x01A720 }, + { "Asia/Dhaka" , 0x01AA70 }, + { "Asia/Dili" , 0x01AB16 }, + { "Asia/Dubai" , 0x01AB9F }, + { "Asia/Dushanbe" , 0x01ABF4 }, + { "Asia/Gaza" , 0x01ACF7 }, + { "Asia/Harbin" , 0x01AF50 }, + { "Asia/Hebron" , 0x01B037 }, + { "Asia/Ho_Chi_Minh" , 0x01B299 }, + { "Asia/Hong_Kong" , 0x01B311 }, + { "Asia/Hovd" , 0x01B4D3 }, + { "Asia/Irkutsk" , 0x01B64B }, + { "Asia/Istanbul" , 0x01B831 }, + { "Asia/Jakarta" , 0x01BC1E }, + { "Asia/Jayapura" , 0x01BCC8 }, + { "Asia/Jerusalem" , 0x01BD64 }, + { "Asia/Kabul" , 0x01C093 }, + { "Asia/Kamchatka" , 0x01C0E4 }, + { "Asia/Karachi" , 0x01C2C0 }, + { "Asia/Kashgar" , 0x01C375 }, + { "Asia/Kathmandu" , 0x01C446 }, + { "Asia/Katmandu" , 0x01C4AC }, + { "Asia/Khandyga" , 0x01C512 }, + { "Asia/Kolkata" , 0x01C737 }, + { "Asia/Krasnoyarsk" , 0x01C7B0 }, + { "Asia/Kuala_Lumpur" , 0x01C998 }, + { "Asia/Kuching" , 0x01CA55 }, + { "Asia/Kuwait" , 0x01CB43 }, + { "Asia/Macao" , 0x01CB98 }, + { "Asia/Macau" , 0x01CCD3 }, + { "Asia/Magadan" , 0x01CE0E }, + { "Asia/Makassar" , 0x01CFF0 }, + { "Asia/Manila" , 0x01D0B4 }, + { "Asia/Muscat" , 0x01D139 }, + { "Asia/Nicosia" , 0x01D18E }, + { "Asia/Novokuznetsk" , 0x01D476 }, + { "Asia/Novosibirsk" , 0x01D678 }, + { "Asia/Omsk" , 0x01D863 }, + { "Asia/Oral" , 0x01DA4A }, + { "Asia/Phnom_Penh" , 0x01DC1A }, + { "Asia/Pontianak" , 0x01DC92 }, + { "Asia/Pyongyang" , 0x01DD53 }, + { "Asia/Qatar" , 0x01DDC0 }, + { "Asia/Qyzylorda" , 0x01DE26 }, + { "Asia/Rangoon" , 0x01DFFC }, + { "Asia/Riyadh" , 0x01E074 }, + { "Asia/Saigon" , 0x01E0C9 }, + { "Asia/Sakhalin" , 0x01E141 }, + { "Asia/Samarkand" , 0x01E338 }, + { "Asia/Seoul" , 0x01E46E }, + { "Asia/Shanghai" , 0x01E512 }, + { "Asia/Singapore" , 0x01E5F2 }, + { "Asia/Taipei" , 0x01E6A9 }, + { "Asia/Tashkent" , 0x01E7C1 }, + { "Asia/Tbilisi" , 0x01E8F2 }, + { "Asia/Tehran" , 0x01EAAC }, + { "Asia/Tel_Aviv" , 0x01ED1A }, + { "Asia/Thimbu" , 0x01F049 }, + { "Asia/Thimphu" , 0x01F0AF }, + { "Asia/Tokyo" , 0x01F115 }, + { "Asia/Ujung_Pandang" , 0x01F19E }, + { "Asia/Ulaanbaatar" , 0x01F21A }, + { "Asia/Ulan_Bator" , 0x01F375 }, + { "Asia/Urumqi" , 0x01F4C2 }, + { "Asia/Ust-Nera" , 0x01F589 }, + { "Asia/Vientiane" , 0x01F78E }, + { "Asia/Vladivostok" , 0x01F806 }, + { "Asia/Yakutsk" , 0x01F9F2 }, + { "Asia/Yekaterinburg" , 0x01FBD7 }, + { "Asia/Yerevan" , 0x01FDE2 }, + { "Atlantic/Azores" , 0x01FFE2 }, + { "Atlantic/Bermuda" , 0x0204E5 }, + { "Atlantic/Canary" , 0x0207C6 }, + { "Atlantic/Cape_Verde" , 0x020A9C }, + { "Atlantic/Faeroe" , 0x020B15 }, + { "Atlantic/Faroe" , 0x020DB9 }, + { "Atlantic/Jan_Mayen" , 0x02105D }, + { "Atlantic/Madeira" , 0x02138F }, + { "Atlantic/Reykjavik" , 0x021898 }, + { "Atlantic/South_Georgia" , 0x021A51 }, + { "Atlantic/St_Helena" , 0x021C63 }, + { "Atlantic/Stanley" , 0x021A95 }, + { "Australia/ACT" , 0x021CB8 }, + { "Australia/Adelaide" , 0x021FD5 }, + { "Australia/Brisbane" , 0x022301 }, + { "Australia/Broken_Hill" , 0x0223C8 }, + { "Australia/Canberra" , 0x022706 }, + { "Australia/Currie" , 0x022A23 }, + { "Australia/Darwin" , 0x022D56 }, + { "Australia/Eucla" , 0x022DDC }, + { "Australia/Hobart" , 0x022EB1 }, + { "Australia/LHI" , 0x02320F }, + { "Australia/Lindeman" , 0x0234AA }, + { "Australia/Lord_Howe" , 0x02358B }, + { "Australia/Melbourne" , 0x023836 }, + { "Australia/North" , 0x023B5B }, + { "Australia/NSW" , 0x023BCF }, + { "Australia/Perth" , 0x023EEC }, + { "Australia/Queensland" , 0x023FC4 }, + { "Australia/South" , 0x024070 }, + { "Australia/Sydney" , 0x02438D }, + { "Australia/Tasmania" , 0x0246CA }, + { "Australia/Victoria" , 0x024A0F }, + { "Australia/West" , 0x024D2C }, + { "Australia/Yancowinna" , 0x024DE2 }, + { "Brazil/Acre" , 0x025104 }, + { "Brazil/DeNoronha" , 0x025203 }, + { "Brazil/East" , 0x025323 }, + { "Brazil/West" , 0x025600 }, + { "Canada/Atlantic" , 0x0256F8 }, + { "Canada/Central" , 0x025BE0 }, + { "Canada/East-Saskatchewan" , 0x0264EA }, + { "Canada/Eastern" , 0x025FFA }, + { "Canada/Mountain" , 0x026673 }, + { "Canada/Newfoundland" , 0x0269E9 }, + { "Canada/Pacific" , 0x026F14 }, + { "Canada/Saskatchewan" , 0x02732D }, + { "Canada/Yukon" , 0x0274B6 }, + { "CET" , 0x0277B9 }, + { "Chile/Continental" , 0x027AC2 }, + { "Chile/EasterIsland" , 0x027E5D }, + { "CST6CDT" , 0x02819F }, + { "Cuba" , 0x0284F0 }, + { "EET" , 0x028863 }, + { "Egypt" , 0x028B16 }, + { "Eire" , 0x028DD9 }, + { "EST" , 0x0292EA }, + { "EST5EDT" , 0x02932E }, + { "Etc/GMT" , 0x02967F }, + { "Etc/GMT+0" , 0x02974B }, + { "Etc/GMT+1" , 0x0297D5 }, + { "Etc/GMT+10" , 0x029862 }, + { "Etc/GMT+11" , 0x0298F0 }, + { "Etc/GMT+12" , 0x02997E }, + { "Etc/GMT+2" , 0x029A99 }, + { "Etc/GMT+3" , 0x029B25 }, + { "Etc/GMT+4" , 0x029BB1 }, + { "Etc/GMT+5" , 0x029C3D }, + { "Etc/GMT+6" , 0x029CC9 }, + { "Etc/GMT+7" , 0x029D55 }, + { "Etc/GMT+8" , 0x029DE1 }, + { "Etc/GMT+9" , 0x029E6D }, + { "Etc/GMT-0" , 0x029707 }, + { "Etc/GMT-1" , 0x02978F }, + { "Etc/GMT-10" , 0x02981B }, + { "Etc/GMT-11" , 0x0298A9 }, + { "Etc/GMT-12" , 0x029937 }, + { "Etc/GMT-13" , 0x0299C5 }, + { "Etc/GMT-14" , 0x029A0C }, + { "Etc/GMT-2" , 0x029A53 }, + { "Etc/GMT-3" , 0x029ADF }, + { "Etc/GMT-4" , 0x029B6B }, + { "Etc/GMT-5" , 0x029BF7 }, + { "Etc/GMT-6" , 0x029C83 }, + { "Etc/GMT-7" , 0x029D0F }, + { "Etc/GMT-8" , 0x029D9B }, + { "Etc/GMT-9" , 0x029E27 }, + { "Etc/GMT0" , 0x0296C3 }, + { "Etc/Greenwich" , 0x029EB3 }, + { "Etc/UCT" , 0x029EF7 }, + { "Etc/Universal" , 0x029F3B }, + { "Etc/UTC" , 0x029F7F }, + { "Etc/Zulu" , 0x029FC3 }, + { "Europe/Amsterdam" , 0x02A007 }, + { "Europe/Andorra" , 0x02A445 }, + { "Europe/Athens" , 0x02A6C1 }, + { "Europe/Belfast" , 0x02AA04 }, + { "Europe/Belgrade" , 0x02AF3B }, + { "Europe/Berlin" , 0x02B204 }, + { "Europe/Bratislava" , 0x02B568 }, + { "Europe/Brussels" , 0x02B89A }, + { "Europe/Bucharest" , 0x02BCD1 }, + { "Europe/Budapest" , 0x02BFFB }, + { "Europe/Busingen" , 0x02C36E }, + { "Europe/Chisinau" , 0x02C625 }, + { "Europe/Copenhagen" , 0x02C9B3 }, + { "Europe/Dublin" , 0x02CCBD }, + { "Europe/Gibraltar" , 0x02D1CE }, + { "Europe/Guernsey" , 0x02D625 }, + { "Europe/Helsinki" , 0x02DB5C }, + { "Europe/Isle_of_Man" , 0x02DE12 }, + { "Europe/Istanbul" , 0x02E349 }, + { "Europe/Jersey" , 0x02E736 }, + { "Europe/Kaliningrad" , 0x02EC6D }, + { "Europe/Kiev" , 0x02EED3 }, + { "Europe/Lisbon" , 0x02F1EA }, + { "Europe/Ljubljana" , 0x02F6EE }, + { "Europe/London" , 0x02F9B7 }, + { "Europe/Luxembourg" , 0x02FEEE }, + { "Europe/Madrid" , 0x030344 }, + { "Europe/Malta" , 0x03070A }, + { "Europe/Mariehamn" , 0x030AC3 }, + { "Europe/Minsk" , 0x030D79 }, + { "Europe/Monaco" , 0x030F87 }, + { "Europe/Moscow" , 0x0313C2 }, + { "Europe/Nicosia" , 0x031613 }, + { "Europe/Oslo" , 0x0318FB }, + { "Europe/Paris" , 0x031C2D }, + { "Europe/Podgorica" , 0x032073 }, + { "Europe/Prague" , 0x03233C }, + { "Europe/Riga" , 0x03266E }, + { "Europe/Rome" , 0x0329B3 }, + { "Europe/Samara" , 0x032D76 }, + { "Europe/San_Marino" , 0x032FA9 }, + { "Europe/Sarajevo" , 0x03336C }, + { "Europe/Simferopol" , 0x033635 }, + { "Europe/Skopje" , 0x033960 }, + { "Europe/Sofia" , 0x033C29 }, + { "Europe/Stockholm" , 0x033F31 }, + { "Europe/Tallinn" , 0x0341E0 }, + { "Europe/Tirane" , 0x03451A }, + { "Europe/Tiraspol" , 0x034820 }, + { "Europe/Uzhgorod" , 0x034BAE }, + { "Europe/Vaduz" , 0x034EC5 }, + { "Europe/Vatican" , 0x035158 }, + { "Europe/Vienna" , 0x03551B }, + { "Europe/Vilnius" , 0x035848 }, + { "Europe/Volgograd" , 0x035B87 }, + { "Europe/Warsaw" , 0x035D87 }, + { "Europe/Zagreb" , 0x036168 }, + { "Europe/Zaporozhye" , 0x036431 }, + { "Europe/Zurich" , 0x036772 }, + { "Factory" , 0x036A21 }, + { "GB" , 0x036A92 }, + { "GB-Eire" , 0x036FC9 }, + { "GMT" , 0x037500 }, + { "GMT+0" , 0x0375CC }, + { "GMT-0" , 0x037588 }, + { "GMT0" , 0x037544 }, + { "Greenwich" , 0x037610 }, + { "Hongkong" , 0x037654 }, + { "HST" , 0x037816 }, + { "Iceland" , 0x03785A }, + { "Indian/Antananarivo" , 0x037A13 }, + { "Indian/Chagos" , 0x037A87 }, + { "Indian/Christmas" , 0x037AE9 }, + { "Indian/Cocos" , 0x037B2D }, + { "Indian/Comoro" , 0x037B71 }, + { "Indian/Kerguelen" , 0x037BC6 }, + { "Indian/Mahe" , 0x037C1B }, + { "Indian/Maldives" , 0x037C70 }, + { "Indian/Mauritius" , 0x037CC5 }, + { "Indian/Mayotte" , 0x037D3B }, + { "Indian/Reunion" , 0x037D90 }, + { "Iran" , 0x037DE5 }, + { "Israel" , 0x038053 }, + { "Jamaica" , 0x038382 }, + { "Japan" , 0x038447 }, + { "Kwajalein" , 0x0384D0 }, + { "Libya" , 0x038533 }, + { "MET" , 0x03872C }, + { "Mexico/BajaNorte" , 0x038A35 }, + { "Mexico/BajaSur" , 0x038D9E }, + { "Mexico/General" , 0x038FE3 }, + { "MST" , 0x039241 }, + { "MST7MDT" , 0x039285 }, + { "Navajo" , 0x0395D6 }, + { "NZ" , 0x03994F }, + { "NZ-CHAT" , 0x039CCD }, + { "Pacific/Apia" , 0x039FB5 }, + { "Pacific/Auckland" , 0x03A151 }, + { "Pacific/Chatham" , 0x03A4DD }, + { "Pacific/Chuuk" , 0x03A7D4 }, + { "Pacific/Easter" , 0x03A82D }, + { "Pacific/Efate" , 0x03AB8B }, + { "Pacific/Enderbury" , 0x03AC51 }, + { "Pacific/Fakaofo" , 0x03ACBF }, + { "Pacific/Fiji" , 0x03AD10 }, + { "Pacific/Funafuti" , 0x03AEA3 }, + { "Pacific/Galapagos" , 0x03AEE7 }, + { "Pacific/Gambier" , 0x03AF5F }, + { "Pacific/Guadalcanal" , 0x03AFC4 }, + { "Pacific/Guam" , 0x03B019 }, + { "Pacific/Honolulu" , 0x03B06F }, + { "Pacific/Johnston" , 0x03B0E6 }, + { "Pacific/Kiritimati" , 0x03B138 }, + { "Pacific/Kosrae" , 0x03B1A3 }, + { "Pacific/Kwajalein" , 0x03B200 }, + { "Pacific/Majuro" , 0x03B26C }, + { "Pacific/Marquesas" , 0x03B2CB }, + { "Pacific/Midway" , 0x03B332 }, + { "Pacific/Nauru" , 0x03B3BC }, + { "Pacific/Niue" , 0x03B434 }, + { "Pacific/Norfolk" , 0x03B492 }, + { "Pacific/Noumea" , 0x03B4E7 }, + { "Pacific/Pago_Pago" , 0x03B577 }, + { "Pacific/Palau" , 0x03B600 }, + { "Pacific/Pitcairn" , 0x03B644 }, + { "Pacific/Pohnpei" , 0x03B699 }, + { "Pacific/Ponape" , 0x03B6EE }, + { "Pacific/Port_Moresby" , 0x03B733 }, + { "Pacific/Rarotonga" , 0x03B777 }, + { "Pacific/Saipan" , 0x03B853 }, + { "Pacific/Samoa" , 0x03B8B6 }, + { "Pacific/Tahiti" , 0x03B93F }, + { "Pacific/Tarawa" , 0x03B9A4 }, + { "Pacific/Tongatapu" , 0x03B9F8 }, + { "Pacific/Truk" , 0x03BA84 }, + { "Pacific/Wake" , 0x03BAC9 }, + { "Pacific/Wallis" , 0x03BB19 }, + { "Pacific/Yap" , 0x03BB5D }, + { "Poland" , 0x03BBA2 }, + { "Portugal" , 0x03BF83 }, + { "PRC" , 0x03C47F }, + { "PST8PDT" , 0x03C530 }, + { "ROC" , 0x03C881 }, + { "ROK" , 0x03C999 }, + { "Singapore" , 0x03CA3D }, + { "Turkey" , 0x03CAF4 }, + { "UCT" , 0x03CEE1 }, + { "Universal" , 0x03CF25 }, + { "US/Alaska" , 0x03CF69 }, + { "US/Aleutian" , 0x03D2D2 }, + { "US/Arizona" , 0x03D638 }, + { "US/Central" , 0x03D6C6 }, + { "US/East-Indiana" , 0x03E0D0 }, + { "US/Eastern" , 0x03DBD1 }, + { "US/Hawaii" , 0x03E33A }, + { "US/Indiana-Starke" , 0x03E3AB }, + { "US/Michigan" , 0x03E71C }, + { "US/Mountain" , 0x03EA53 }, + { "US/Pacific" , 0x03EDCC }, + { "US/Pacific-New" , 0x03F1D1 }, + { "US/Samoa" , 0x03F5D6 }, + { "UTC" , 0x03F65F }, + { "W-SU" , 0x03F956 }, + { "WET" , 0x03F6A3 }, + { "Zulu" , 0x03FB90 }, }; /* This is a generated file, do not modify */ -const unsigned char timelib_timezone_db_data_builtin[260756] = { +const unsigned char timelib_timezone_db_data_builtin[261076] = { /* Africa/Abidjan */ @@ -758,7 +758,7 @@ const unsigned char timelib_timezone_db_data_builtin[260756] = { /* Africa/Casablanca */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x4D, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x96, 0x51, 0xF9, 0x9C, +0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x96, 0x51, 0xF9, 0x9C, 0xC6, 0xFF, 0x14, 0x80, 0xC7, 0x58, 0xAC, 0x70, 0xC7, 0xD9, 0xED, 0x80, 0xD2, 0xA1, 0x32, 0xF0, 0xDB, 0x35, 0xA4, 0x00, 0xDB, 0xEE, 0x27, 0xF0, 0xFB, 0x25, 0x72, 0x40, 0xFB, 0xC2, 0xEF, 0x70, 0x08, 0x6B, 0x84, 0x80, 0x08, 0xC6, 0x6D, 0xF0, 0x0B, 0xE8, 0x0C, 0x00, 0x0C, 0x61, 0x47, 0xF0, @@ -766,28 +766,33 @@ const unsigned char timelib_timezone_db_data_builtin[260756] = { 0x1A, 0xB7, 0xA6, 0x00, 0x1E, 0x18, 0x6F, 0xF0, 0x48, 0x41, 0xE6, 0x80, 0x48, 0xBB, 0x22, 0x70, 0x4A, 0x23, 0x1A, 0x00, 0x4A, 0x8D, 0xD5, 0x70, 0x4B, 0xDC, 0xC0, 0x80, 0x4C, 0x5D, 0xE5, 0x70, 0x4D, 0x97, 0xB8, 0x80, 0x4E, 0x34, 0x8C, 0xF0, 0x4F, 0x9C, 0xA0, 0xA0, 0x50, 0x08, 0xBB, 0xA0, -0x50, 0x31, 0x9A, 0x20, 0x50, 0x67, 0xA7, 0xA0, 0x51, 0x7C, 0x82, 0xA0, 0x52, 0x47, 0x89, 0xA0, -0x53, 0x5C, 0x64, 0xA0, 0x54, 0x27, 0x6B, 0xA0, 0x55, 0x3C, 0x46, 0xA0, 0x56, 0x07, 0x4D, 0xA0, -0x57, 0x1C, 0x28, 0xA0, 0x57, 0xE7, 0x2F, 0xA0, 0x59, 0x05, 0x45, 0x20, 0x59, 0xC7, 0x11, 0xA0, -0x5A, 0xE5, 0x27, 0x20, 0x5B, 0xB0, 0x2E, 0x20, 0x5C, 0xC5, 0x09, 0x20, 0x5D, 0x90, 0x10, 0x20, -0x5E, 0xA4, 0xEB, 0x20, 0x5F, 0x6F, 0xF2, 0x20, 0x60, 0x84, 0xCD, 0x20, 0x61, 0x4F, 0xD4, 0x20, -0x62, 0x64, 0xAF, 0x20, 0x63, 0x2F, 0xB6, 0x20, 0x64, 0x4D, 0xCB, 0xA0, 0x65, 0x0F, 0x98, 0x20, -0x66, 0x2D, 0xAD, 0xA0, 0x66, 0xF8, 0xB4, 0xA0, 0x68, 0x0D, 0x8F, 0xA0, 0x68, 0xD8, 0x96, 0xA0, -0x69, 0xED, 0x71, 0xA0, 0x6A, 0xB8, 0x78, 0xA0, 0x6B, 0xCD, 0x53, 0xA0, 0x6C, 0x98, 0x5A, 0xA0, -0x6D, 0xB6, 0x70, 0x20, 0x6E, 0x78, 0x3C, 0xA0, 0x6F, 0x96, 0x52, 0x20, 0x70, 0x61, 0x59, 0x20, -0x71, 0x76, 0x34, 0x20, 0x72, 0x41, 0x3B, 0x20, 0x73, 0x56, 0x16, 0x20, 0x74, 0x21, 0x1D, 0x20, -0x75, 0x35, 0xF8, 0x20, 0x76, 0x00, 0xFF, 0x20, 0x77, 0x15, 0xDA, 0x20, 0x77, 0xE0, 0xE1, 0x20, -0x78, 0xFE, 0xF6, 0xA0, 0x79, 0xC0, 0xC3, 0x20, 0x7A, 0xDE, 0xD8, 0xA0, 0x7B, 0xA9, 0xDF, 0xA0, -0x7C, 0xBE, 0xBA, 0xA0, 0x7D, 0x89, 0xC1, 0xA0, 0x7E, 0x9E, 0x9C, 0xA0, 0x7F, 0x69, 0xA3, 0xA0, +0x50, 0x31, 0x9A, 0x20, 0x50, 0x67, 0xA7, 0xA0, 0x51, 0x7C, 0x82, 0xA0, 0x51, 0xDB, 0x6E, 0xA0, +0x52, 0x02, 0xFB, 0xA0, 0x52, 0x47, 0x89, 0xA0, 0x53, 0x5C, 0x64, 0xA0, 0x53, 0xAF, 0x73, 0x20, +0x53, 0xD7, 0x00, 0x20, 0x54, 0x27, 0x6B, 0xA0, 0x55, 0x3C, 0x46, 0xA0, 0x55, 0x82, 0x26, 0x20, +0x55, 0xA9, 0xB3, 0x20, 0x56, 0x07, 0x4D, 0xA0, 0x57, 0x1C, 0x28, 0xA0, 0x57, 0x56, 0x2A, 0xA0, +0x57, 0x7D, 0xB7, 0xA0, 0x57, 0xE7, 0x2F, 0xA0, 0x59, 0x05, 0x45, 0x20, 0x59, 0x28, 0xDD, 0xA0, +0x59, 0x50, 0x6A, 0xA0, 0x59, 0xC7, 0x11, 0xA0, 0x5A, 0xE5, 0x27, 0x20, 0x5A, 0xFB, 0x90, 0xA0, +0x5B, 0x23, 0x1D, 0xA0, 0x5B, 0xB0, 0x2E, 0x20, 0x5C, 0xC5, 0x09, 0x20, 0x5C, 0xCF, 0x95, 0x20, +0x5C, 0xF7, 0x22, 0x20, 0x5D, 0x90, 0x10, 0x20, 0x5E, 0xC9, 0xD5, 0x20, 0x5F, 0x6F, 0xF2, 0x20, +0x60, 0x9C, 0x88, 0x20, 0x61, 0x4F, 0xD4, 0x20, 0x62, 0x70, 0x8C, 0xA0, 0x63, 0x2F, 0xB6, 0x20, +0x64, 0x4D, 0xCB, 0xA0, 0x65, 0x0F, 0x98, 0x20, 0x66, 0x2D, 0xAD, 0xA0, 0x66, 0xF8, 0xB4, 0xA0, +0x68, 0x0D, 0x8F, 0xA0, 0x68, 0xD8, 0x96, 0xA0, 0x69, 0xED, 0x71, 0xA0, 0x6A, 0xB8, 0x78, 0xA0, +0x6B, 0xCD, 0x53, 0xA0, 0x6C, 0x98, 0x5A, 0xA0, 0x6D, 0xB6, 0x70, 0x20, 0x6E, 0x78, 0x3C, 0xA0, +0x6F, 0x96, 0x52, 0x20, 0x70, 0x61, 0x59, 0x20, 0x71, 0x76, 0x34, 0x20, 0x72, 0x41, 0x3B, 0x20, +0x73, 0x56, 0x16, 0x20, 0x74, 0x21, 0x1D, 0x20, 0x75, 0x35, 0xF8, 0x20, 0x76, 0x00, 0xFF, 0x20, +0x77, 0x15, 0xDA, 0x20, 0x77, 0xE0, 0xE1, 0x20, 0x78, 0xFE, 0xF6, 0xA0, 0x79, 0xC0, 0xC3, 0x20, +0x7A, 0xDE, 0xD8, 0xA0, 0x7B, 0xA9, 0xDF, 0xA0, 0x7C, 0xBE, 0xBA, 0xA0, 0x7D, 0x89, 0xC1, 0xA0, +0x7E, 0x9E, 0x9C, 0xA0, 0x7F, 0x69, 0xA3, 0xA0, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0x03, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, -0x02, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x01, 0x04, 0x00, 0x00, 0x00, -0x00, 0x00, 0x09, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x0D, 0x4C, 0x4D, 0x54, 0x00, 0x57, 0x45, 0x53, -0x54, 0x00, 0x57, 0x45, 0x54, 0x00, 0x43, 0x45, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0xBC, 0xAC, 0xC8, 0x01, 0x07, 0x16, 0x42, 0x00, 0x00, 0x00, 0x00, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0xF8, 0xE4, 0x00, 0x00, 0x00, 0x00, 0x0E, +0x10, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x0D, 0x4C, +0x4D, 0x54, 0x00, 0x57, 0x45, 0x53, 0x54, 0x00, 0x57, 0x45, 0x54, 0x00, 0x43, 0x45, 0x54, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBC, 0xAC, 0xC8, 0x01, 0x07, 0x16, 0x42, +0x00, 0x00, 0x00, 0x00, /* Africa/Ceuta */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x45, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1863,7 +1868,7 @@ const unsigned char timelib_timezone_db_data_builtin[260756] = { 0x45, 0x31, 0xB2, 0x40, 0x45, 0xF3, 0x70, 0xB0, 0x47, 0x1A, 0xCE, 0xC0, 0x47, 0xD3, 0x52, 0xB0, 0x48, 0xFA, 0xB0, 0xC0, 0x49, 0xB3, 0x34, 0xB0, 0x4A, 0xDA, 0x92, 0xC0, 0x4B, 0xC1, 0x3B, 0x30, 0x4C, 0xA7, 0xFF, 0xC0, 0x4D, 0xA1, 0x1D, 0x30, 0x4E, 0x87, 0xE1, 0xC0, 0x4F, 0x80, 0xFF, 0x30, -0x50, 0x70, 0xFE, 0x40, 0x51, 0x6A, 0x1B, 0xB0, 0x52, 0x50, 0xE0, 0x40, 0x53, 0x49, 0xFD, 0xB0, +0x50, 0x70, 0xFE, 0x40, 0x51, 0x4E, 0x6C, 0x30, 0x52, 0x50, 0xE0, 0x40, 0x53, 0x49, 0xFD, 0xB0, 0x54, 0x30, 0xC2, 0x40, 0x55, 0x29, 0xDF, 0xB0, 0x56, 0x10, 0xA4, 0x40, 0x57, 0x09, 0xC1, 0xB0, 0x57, 0xF0, 0x86, 0x40, 0x58, 0xE9, 0xA3, 0xB0, 0x59, 0xD0, 0x68, 0x40, 0x5A, 0xC9, 0x85, 0xB0, 0x5B, 0xB9, 0x84, 0xC0, 0x5C, 0xB2, 0xA2, 0x30, 0x5D, 0x99, 0x66, 0xC0, 0x5E, 0x92, 0x84, 0x30, @@ -5743,7 +5748,7 @@ const unsigned char timelib_timezone_db_data_builtin[260756] = { /* America/Port-au-Prince */ 0x50, 0x48, 0x50, 0x31, 0x01, 0x48, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x9C, 0x6E, 0x71, 0xFC, +0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x9C, 0x6E, 0x71, 0xFC, 0x19, 0x1B, 0x46, 0xD0, 0x1A, 0x01, 0xEF, 0x40, 0x1A, 0xF1, 0xEE, 0x50, 0x1B, 0xE1, 0xD1, 0x40, 0x1C, 0xD1, 0xD0, 0x50, 0x1D, 0xC1, 0xB3, 0x40, 0x1E, 0xB1, 0xB2, 0x50, 0x1F, 0xA1, 0x95, 0x40, 0x20, 0x91, 0x94, 0x50, 0x21, 0x81, 0x77, 0x40, 0x22, 0x55, 0xD4, 0xE0, 0x23, 0x6A, 0xAF, 0xE0, @@ -5753,13 +5758,29 @@ const unsigned char timelib_timezone_db_data_builtin[260756] = { 0x2F, 0x7E, 0x3D, 0x60, 0x30, 0x93, 0x18, 0x60, 0x31, 0x67, 0x59, 0xE0, 0x32, 0x72, 0xFA, 0x60, 0x33, 0x47, 0x3B, 0xE0, 0x34, 0x52, 0xDC, 0x60, 0x42, 0x4F, 0x78, 0x50, 0x43, 0x64, 0x45, 0x40, 0x44, 0x2F, 0x5A, 0x50, 0x45, 0x44, 0x27, 0x40, 0x4F, 0x5C, 0x4D, 0x70, 0x50, 0x96, 0x04, 0x60, -0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x03, 0x04, 0x03, 0x04, 0x03, -0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x01, -0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, 0xFF, 0xBC, 0x44, 0x00, 0x00, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, -0x05, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x09, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x05, 0xFF, 0xFF, 0xB9, -0xB0, 0x00, 0x09, 0x50, 0x50, 0x4D, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, -0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0x9B, 0xD5, 0x00, 0xA4, -0x49, 0x4A, 0x00, 0x00, 0x00, 0x00, +0x51, 0x3C, 0x2F, 0x70, 0x52, 0x75, 0xE6, 0x60, 0x53, 0x1C, 0x11, 0x70, 0x54, 0x55, 0xC8, 0x60, +0x54, 0xFB, 0xF3, 0x70, 0x56, 0x35, 0xAA, 0x60, 0x56, 0xE5, 0x0F, 0xF0, 0x58, 0x1E, 0xC6, 0xE0, +0x58, 0xC4, 0xF1, 0xF0, 0x59, 0xFE, 0xA8, 0xE0, 0x5A, 0xA4, 0xD3, 0xF0, 0x5B, 0xDE, 0x8A, 0xE0, +0x5C, 0x84, 0xB5, 0xF0, 0x5D, 0xBE, 0x6C, 0xE0, 0x5E, 0x64, 0x97, 0xF0, 0x5F, 0x9E, 0x4E, 0xE0, +0x60, 0x4D, 0xB4, 0x70, 0x61, 0x87, 0x6B, 0x60, 0x62, 0x2D, 0x96, 0x70, 0x63, 0x67, 0x4D, 0x60, +0x64, 0x0D, 0x78, 0x70, 0x65, 0x47, 0x2F, 0x60, 0x65, 0xED, 0x5A, 0x70, 0x67, 0x27, 0x11, 0x60, +0x67, 0xCD, 0x3C, 0x70, 0x69, 0x06, 0xF3, 0x60, 0x69, 0xAD, 0x1E, 0x70, 0x6A, 0xE6, 0xD5, 0x60, +0x6B, 0x96, 0x3A, 0xF0, 0x6C, 0xCF, 0xF1, 0xE0, 0x6D, 0x76, 0x1C, 0xF0, 0x6E, 0xAF, 0xD3, 0xE0, +0x6F, 0x55, 0xFE, 0xF0, 0x70, 0x8F, 0xB5, 0xE0, 0x71, 0x35, 0xE0, 0xF0, 0x72, 0x6F, 0x97, 0xE0, +0x73, 0x15, 0xC2, 0xF0, 0x74, 0x4F, 0x79, 0xE0, 0x74, 0xFE, 0xDF, 0x70, 0x76, 0x38, 0x96, 0x60, +0x76, 0xDE, 0xC1, 0x70, 0x78, 0x18, 0x78, 0x60, 0x78, 0xBE, 0xA3, 0x70, 0x79, 0xF8, 0x5A, 0x60, +0x7A, 0x9E, 0x85, 0x70, 0x7B, 0xD8, 0x3C, 0x60, 0x7C, 0x7E, 0x67, 0x70, 0x7D, 0xB8, 0x1E, 0x60, +0x7E, 0x5E, 0x49, 0x70, 0x7F, 0x98, 0x00, 0x60, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x03, +0x04, 0x03, 0x04, 0x03, 0x04, 0x03, 0x04, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, +0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0xFF, +0xFF, 0xBC, 0x44, 0x00, 0x00, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x05, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, +0x09, 0xFF, 0xFF, 0xC7, 0xC0, 0x01, 0x05, 0xFF, 0xFF, 0xB9, 0xB0, 0x00, 0x09, 0x50, 0x50, 0x4D, +0x54, 0x00, 0x45, 0x44, 0x54, 0x00, 0x45, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xA5, 0x9B, 0xD5, 0x00, 0xA4, 0x49, 0x4A, 0x00, 0x00, 0x00, 0x00, + /* America/Porto_Acre */ 0x50, 0x48, 0x50, 0x31, 0x00, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -18361,4 +18382,4 @@ const unsigned char timelib_timezone_db_data_builtin[260756] = { 0x00, 0x00, 0x55, 0x54, 0x43, 0x00, 0x00, 0x00, 0x00, 0x89, 0x54, 0x40, 0x01, 0x12, 0xA8, 0x80, 0x00, 0x00, 0x00, 0x00, }; -const timelib_tzdb timezonedb_builtin = { "2013.1", 579, timezonedb_idx_builtin, timelib_timezone_db_data_builtin }; +const timelib_tzdb timezonedb_builtin = { "2013.2", 579, timezonedb_idx_builtin, timelib_timezone_db_data_builtin }; diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 71958578d..a073aa691 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -39,6 +39,20 @@ static __inline __int64_t php_date_llabs( __int64_t i ) { return i >= 0 ? i : -i static inline long long php_date_llabs( long long i ) { return i >= 0 ? i : -i; } #endif +#ifdef PHP_WIN32 +#define DATE_I64_BUF_LEN 65 +# define DATE_I64A(i, s, len) _i64toa_s(i, s, len, 10) +# define DATE_A64I(i, s) i = _atoi64(s) +#else +#define DATE_I64_BUF_LEN 65 +# define DATE_I64A(i, s, len) \ + do { \ + int st = snprintf(s, len, "%lld", i); \ + s[st] = '\0'; \ + } while (0); +# define DATE_A64I(i, s) i = atoll(s) +#endif + /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_date, 0, 0, 1) ZEND_ARG_INFO(0, format) @@ -488,6 +502,8 @@ const zend_function_entry date_funcs_interval[] = { const zend_function_entry date_funcs_period[] = { PHP_ME(DatePeriod, __construct, arginfo_date_period_construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) + PHP_ME(DatePeriod, __wakeup, NULL, ZEND_ACC_PUBLIC) + PHP_ME(DatePeriod, __set_state, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_FE_END }; @@ -594,9 +610,13 @@ static HashTable *date_object_get_gc(zval *object, zval ***table, int *n TSRMLS_ static HashTable *date_object_get_properties(zval *object TSRMLS_DC); static HashTable *date_object_get_gc_interval(zval *object, zval ***table, int *n TSRMLS_DC); static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC); +static HashTable *date_object_get_gc_period(zval *object, zval ***table, int *n TSRMLS_DC); +static HashTable *date_object_get_properties_period(zval *object TSRMLS_DC); zval *date_interval_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC); void date_interval_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC); +static zval *date_period_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC); +static void date_period_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC); /* {{{ Module struct */ zend_module_entry date_module_entry = { @@ -1867,11 +1887,10 @@ static void date_period_it_current_data(zend_object_iterator *iter, zval ***data /* {{{ date_period_it_current_key */ -static int date_period_it_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) +static void date_period_it_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) { - date_period_it *iterator = (date_period_it *)iter; - *int_key = iterator->current_index; - return HASH_KEY_IS_LONG; + date_period_it *iterator = (date_period_it *)iter; + ZVAL_LONG(key, iterator->current_index); } /* }}} */ @@ -2013,6 +2032,11 @@ static void date_register_classes(TSRMLS_D) zend_class_implements(date_ce_period TSRMLS_CC, 1, zend_ce_traversable); memcpy(&date_object_handlers_period, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); date_object_handlers_period.clone_obj = date_object_clone_period; + date_object_handlers_period.get_properties = date_object_get_properties_period; + date_object_handlers_period.get_property_ptr_ptr = NULL; + date_object_handlers_period.get_gc = date_object_get_gc_period; + date_object_handlers_period.read_property = date_period_read_property; + date_object_handlers_period.write_property = date_period_write_property; #define REGISTER_PERIOD_CLASS_CONST_STRING(const_name, value) \ zend_declare_class_constant_long(date_ce_period, const_name, sizeof(const_name)-1, value TSRMLS_CC); @@ -2125,7 +2149,7 @@ static HashTable *date_object_get_properties(zval *object TSRMLS_DC) props = zend_std_get_properties(object TSRMLS_CC); - if (!dateobj->time) { + if (!dateobj->time || GC_G(gc_active)) { return props; } @@ -2273,7 +2297,6 @@ static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC) zval *zv; php_interval_obj *intervalobj; - intervalobj = (php_interval_obj *) zend_object_store_get_object(object TSRMLS_CC); props = zend_std_get_properties(object TSRMLS_CC); @@ -2282,6 +2305,15 @@ static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC) return props; } +#define PHP_DATE_INTERVAL_ADD_PROPERTY_I64(n, f) \ + do { \ + char i64_buf[DATE_I64_BUF_LEN]; \ + MAKE_STD_ZVAL(zv); \ + DATE_I64A(intervalobj->diff->f, i64_buf, DATE_I64_BUF_LEN); \ + ZVAL_STRING(zv, i64_buf, 1); \ + zend_hash_update(props, n, strlen(n) + 1, &zv, sizeof(zval), NULL); \ + } while(0); + #define PHP_DATE_INTERVAL_ADD_PROPERTY(n,f) \ MAKE_STD_ZVAL(zv); \ ZVAL_LONG(zv, intervalobj->diff->f); \ @@ -2293,14 +2325,21 @@ static HashTable *date_object_get_properties_interval(zval *object TSRMLS_DC) PHP_DATE_INTERVAL_ADD_PROPERTY("h", h); PHP_DATE_INTERVAL_ADD_PROPERTY("i", i); PHP_DATE_INTERVAL_ADD_PROPERTY("s", s); + PHP_DATE_INTERVAL_ADD_PROPERTY("weekday", weekday); + PHP_DATE_INTERVAL_ADD_PROPERTY("weekday_behavior", weekday_behavior); + PHP_DATE_INTERVAL_ADD_PROPERTY("first_last_day_of", first_last_day_of); PHP_DATE_INTERVAL_ADD_PROPERTY("invert", invert); if (intervalobj->diff->days != -99999) { - PHP_DATE_INTERVAL_ADD_PROPERTY("days", days); + PHP_DATE_INTERVAL_ADD_PROPERTY_I64("days", days); } else { MAKE_STD_ZVAL(zv); ZVAL_FALSE(zv); zend_hash_update(props, "days", 5, &zv, sizeof(zval), NULL); } + PHP_DATE_INTERVAL_ADD_PROPERTY("special_type", special.type); + PHP_DATE_INTERVAL_ADD_PROPERTY_I64("special_amount", special.amount); + PHP_DATE_INTERVAL_ADD_PROPERTY("have_weekday_relative", have_weekday_relative); + PHP_DATE_INTERVAL_ADD_PROPERTY("have_special_relative", have_special_relative); return props; } @@ -2402,6 +2441,7 @@ PHPAPI zval *php_date_instantiate(zend_class_entry *pce, zval *object TSRMLS_DC) object_init_ex(object, pce); Z_SET_REFCOUNT_P(object, 1); Z_UNSET_ISREF_P(object); + return object; } @@ -2634,13 +2674,15 @@ static int php_date_initialize_from_hash(zval **return_value, php_date_obj **dat case TIMELIB_ZONETYPE_OFFSET: case TIMELIB_ZONETYPE_ABBR: { char *tmp = emalloc(Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2); + int ret; snprintf(tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 2, "%s %s", Z_STRVAL_PP(z_date), Z_STRVAL_PP(z_timezone)); - php_date_initialize(*dateobj, tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 1, NULL, NULL, 0 TSRMLS_CC); + ret = php_date_initialize(*dateobj, tmp, Z_STRLEN_PP(z_date) + Z_STRLEN_PP(z_timezone) + 1, NULL, NULL, 0 TSRMLS_CC); efree(tmp); - return 1; + return 1 == ret; } - case TIMELIB_ZONETYPE_ID: + case TIMELIB_ZONETYPE_ID: { + int ret; convert_to_string(*z_timezone); tzi = php_date_parse_tzfile(Z_STRVAL_PP(z_timezone), DATE_TIMEZONEDB TSRMLS_CC); @@ -2651,9 +2693,10 @@ static int php_date_initialize_from_hash(zval **return_value, php_date_obj **dat tzobj->tzi.tz = tzi; tzobj->initialized = 1; - php_date_initialize(*dateobj, Z_STRVAL_PP(z_date), Z_STRLEN_PP(z_date), NULL, tmp_obj, 0 TSRMLS_CC); + ret = php_date_initialize(*dateobj, Z_STRVAL_PP(z_date), Z_STRLEN_PP(z_date), NULL, tmp_obj, 0 TSRMLS_CC); zval_ptr_dtor(&tmp_obj); - return 1; + return 1 == ret; + } } } } @@ -2677,7 +2720,9 @@ PHP_METHOD(DateTime, __set_state) php_date_instantiate(date_ce_date, return_value TSRMLS_CC); dateobj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC); - php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC); + if (!php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC)) { + php_error(E_ERROR, "Invalid serialization data for DateTime object"); + } } /* }}} */ @@ -2697,7 +2742,9 @@ PHP_METHOD(DateTimeImmutable, __set_state) php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC); dateobj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC); - php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC); + if (!php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC)) { + php_error(E_ERROR, "Invalid serialization data for DateTimeImmutable object"); + } } /* }}} */ @@ -2713,7 +2760,9 @@ PHP_METHOD(DateTime, __wakeup) myht = Z_OBJPROP_P(object); - php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC); + if (!php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC)) { + php_error(E_ERROR, "Invalid serialization data for DateTime object"); + } } /* }}} */ @@ -3952,30 +4001,48 @@ PHP_METHOD(DateInterval, __construct) } /* }}} */ -static long php_date_long_from_hash_element(HashTable *myht, char *element, size_t size) -{ - zval **z_arg = NULL; - - if (zend_hash_find(myht, element, size + 1, (void**) &z_arg) == SUCCESS) { - convert_to_long(*z_arg); - return Z_LVAL_PP(z_arg); - } else { - return -1; - } -} static int php_date_interval_initialize_from_hash(zval **return_value, php_interval_obj **intobj, HashTable *myht TSRMLS_DC) { (*intobj)->diff = timelib_rel_time_ctor(); - (*intobj)->diff->y = php_date_long_from_hash_element(myht, "y", 1); - (*intobj)->diff->m = php_date_long_from_hash_element(myht, "m", 1); - (*intobj)->diff->d = php_date_long_from_hash_element(myht, "d", 1); - (*intobj)->diff->h = php_date_long_from_hash_element(myht, "h", 1); - (*intobj)->diff->i = php_date_long_from_hash_element(myht, "i", 1); - (*intobj)->diff->s = php_date_long_from_hash_element(myht, "s", 1); - (*intobj)->diff->invert = php_date_long_from_hash_element(myht, "invert", 6); - (*intobj)->diff->days = php_date_long_from_hash_element(myht, "days", 4); +#define PHP_DATE_INTERVAL_READ_PROPERTY(element, member, itype, def) \ + do { \ + zval **z_arg = NULL; \ + if (zend_hash_find(myht, element, strlen(element) + 1, (void**) &z_arg) == SUCCESS) { \ + convert_to_long(*z_arg); \ + (*intobj)->diff->member = (itype)Z_LVAL_PP(z_arg); \ + } else { \ + (*intobj)->diff->member = (itype)def; \ + } \ + } while (0); + +#define PHP_DATE_INTERVAL_READ_PROPERTY_I64(element, member) \ + do { \ + zval **z_arg = NULL; \ + if (zend_hash_find(myht, element, strlen(element) + 1, (void**) &z_arg) == SUCCESS) { \ + convert_to_string(*z_arg); \ + DATE_A64I((*intobj)->diff->member, Z_STRVAL_PP(z_arg)); \ + } else { \ + (*intobj)->diff->member = -1LL; \ + } \ + } while (0); + + PHP_DATE_INTERVAL_READ_PROPERTY("y", y, timelib_sll, -1) + PHP_DATE_INTERVAL_READ_PROPERTY("m", m, timelib_sll, -1) + PHP_DATE_INTERVAL_READ_PROPERTY("d", d, timelib_sll, -1) + PHP_DATE_INTERVAL_READ_PROPERTY("h", h, timelib_sll, -1) + PHP_DATE_INTERVAL_READ_PROPERTY("i", i, timelib_sll, -1) + PHP_DATE_INTERVAL_READ_PROPERTY("s", s, timelib_sll, -1) + PHP_DATE_INTERVAL_READ_PROPERTY("weekday", weekday, int, -1) + PHP_DATE_INTERVAL_READ_PROPERTY("weekday_behavior", weekday_behavior, int, -1) + PHP_DATE_INTERVAL_READ_PROPERTY("first_last_day_of", first_last_day_of, int, -1) + PHP_DATE_INTERVAL_READ_PROPERTY("invert", invert, int, 0); + PHP_DATE_INTERVAL_READ_PROPERTY_I64("days", days); + PHP_DATE_INTERVAL_READ_PROPERTY("special_type", special.type, unsigned int, 0); + PHP_DATE_INTERVAL_READ_PROPERTY_I64("special_amount", special.amount); + PHP_DATE_INTERVAL_READ_PROPERTY("have_weekday_relative", have_weekday_relative, unsigned int, 0); + PHP_DATE_INTERVAL_READ_PROPERTY("have_special_relative", have_special_relative, unsigned int, 0); (*intobj)->initialized = 1; return 0; @@ -4581,6 +4648,230 @@ PHP_FUNCTION(date_sun_info) timelib_time_dtor(t2); } /* }}} */ + +static HashTable *date_object_get_gc_period(zval *object, zval ***table, int *n TSRMLS_DC) +{ + *table = NULL; + *n = 0; + return zend_std_get_properties(object TSRMLS_CC); +} + +static HashTable *date_object_get_properties_period(zval *object TSRMLS_DC) +{ + HashTable *props; + zval *zv; + php_period_obj *period_obj; + + period_obj = zend_object_store_get_object(object TSRMLS_CC); + + props = zend_std_get_properties(object TSRMLS_CC); + + if (!period_obj->start || GC_G(gc_active)) { + return props; + } + + MAKE_STD_ZVAL(zv); + if (period_obj->start) { + php_date_obj *date_obj; + object_init_ex(zv, date_ce_date); + date_obj = zend_object_store_get_object(zv TSRMLS_CC); + date_obj->time = timelib_time_clone(period_obj->start); + } else { + ZVAL_NULL(zv); + } + zend_hash_update(props, "start", sizeof("start"), &zv, sizeof(zv), NULL); + + MAKE_STD_ZVAL(zv); + if (period_obj->current) { + php_date_obj *date_obj; + object_init_ex(zv, date_ce_date); + date_obj = zend_object_store_get_object(zv TSRMLS_CC); + date_obj->time = timelib_time_clone(period_obj->current); + } else { + ZVAL_NULL(zv); + } + zend_hash_update(props, "current", sizeof("current"), &zv, sizeof(zv), NULL); + + MAKE_STD_ZVAL(zv); + if (period_obj->end) { + php_date_obj *date_obj; + object_init_ex(zv, date_ce_date); + date_obj = zend_object_store_get_object(zv TSRMLS_CC); + date_obj->time = timelib_time_clone(period_obj->end); + } else { + ZVAL_NULL(zv); + } + zend_hash_update(props, "end", sizeof("end"), &zv, sizeof(zv), NULL); + + MAKE_STD_ZVAL(zv); + if (period_obj->interval) { + php_interval_obj *interval_obj; + object_init_ex(zv, date_ce_interval); + interval_obj = zend_object_store_get_object(zv TSRMLS_CC); + interval_obj->diff = timelib_rel_time_clone(period_obj->interval); + interval_obj->initialized = 1; + } else { + ZVAL_NULL(zv); + } + zend_hash_update(props, "interval", sizeof("interval"), &zv, sizeof(zv), NULL); + + /* converted to larger type (int->long); must check when unserializing */ + MAKE_STD_ZVAL(zv); + ZVAL_LONG(zv, (long) period_obj->recurrences); + zend_hash_update(props, "recurrences", sizeof("recurrences"), &zv, sizeof(zv), NULL); + + MAKE_STD_ZVAL(zv); + ZVAL_BOOL(zv, period_obj->include_start_date); + zend_hash_update(props, "include_start_date", sizeof("include_start_date"), &zv, sizeof(zv), NULL); + + return props; +} + +static int php_date_period_initialize_from_hash(php_period_obj *period_obj, HashTable *myht TSRMLS_DC) +{ + zval **ht_entry; + + /* this function does no rollback on error */ + + if (zend_hash_find(myht, "start", sizeof("start"), (void**) &ht_entry) == SUCCESS) { + if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) { + php_date_obj *date_obj; + date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC); + period_obj->start = timelib_time_clone(date_obj->time); + period_obj->start_ce = Z_OBJCE_PP(ht_entry); + } else if (Z_TYPE_PP(ht_entry) != IS_NULL) { + return 0; + } + } else { + return 0; + } + + if (zend_hash_find(myht, "end", sizeof("end"), (void**) &ht_entry) == SUCCESS) { + if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) { + php_date_obj *date_obj; + date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC); + period_obj->end = timelib_time_clone(date_obj->time); + } else if (Z_TYPE_PP(ht_entry) != IS_NULL) { + return 0; + } + } else { + return 0; + } + + if (zend_hash_find(myht, "current", sizeof("current"), (void**) &ht_entry) == SUCCESS) { + if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_date) { + php_date_obj *date_obj; + date_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC); + period_obj->current = timelib_time_clone(date_obj->time); + } else if (Z_TYPE_PP(ht_entry) != IS_NULL) { + return 0; + } + } else { + return 0; + } + + if (zend_hash_find(myht, "interval", sizeof("interval"), (void**) &ht_entry) == SUCCESS) { + if (Z_TYPE_PP(ht_entry) == IS_OBJECT && Z_OBJCE_PP(ht_entry) == date_ce_interval) { + php_interval_obj *interval_obj; + interval_obj = zend_object_store_get_object(*ht_entry TSRMLS_CC); + period_obj->interval = timelib_rel_time_clone(interval_obj->diff); + } else { /* interval is required */ + return 0; + } + } else { + return 0; + } + + if (zend_hash_find(myht, "recurrences", sizeof("recurrences"), (void**) &ht_entry) == SUCCESS && + Z_TYPE_PP(ht_entry) == IS_LONG && Z_LVAL_PP(ht_entry) >= 0 && Z_LVAL_PP(ht_entry) <= INT_MAX) { + period_obj->recurrences = Z_LVAL_PP(ht_entry); + } else { + return 0; + } + + if (zend_hash_find(myht, "include_start_date", sizeof("include_start_date"), (void**) &ht_entry) == SUCCESS && + Z_TYPE_PP(ht_entry) == IS_BOOL) { + period_obj->include_start_date = Z_BVAL_PP(ht_entry); + } else { + return 0; + } + + period_obj->initialized = 1; + + return 1; +} + +/* {{{ proto DatePeriod::__set_state() +*/ +PHP_METHOD(DatePeriod, __set_state) +{ + php_period_obj *period_obj; + zval *array; + HashTable *myht; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) { + RETURN_FALSE; + } + + myht = Z_ARRVAL_P(array); + + object_init_ex(return_value, date_ce_period); + period_obj = zend_object_store_get_object(return_value TSRMLS_CC); + if (!php_date_period_initialize_from_hash(period_obj, myht TSRMLS_CC)) { + php_error(E_ERROR, "Invalid serialization data for DatePeriod object"); + } +} +/* }}} */ + +/* {{{ proto DatePeriod::__wakeup() +*/ +PHP_METHOD(DatePeriod, __wakeup) +{ + zval *object = getThis(); + php_period_obj *period_obj; + HashTable *myht; + + period_obj = zend_object_store_get_object(object TSRMLS_CC); + + myht = Z_OBJPROP_P(object); + + if (!php_date_period_initialize_from_hash(period_obj, myht TSRMLS_CC)) { + php_error(E_ERROR, "Invalid serialization data for DatePeriod object"); + } +} +/* }}} */ + +/* {{{ date_period_read_property */ +static zval *date_period_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC) +{ + zval *zv; + if (type != BP_VAR_IS && type != BP_VAR_R) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Retrieval of DatePeriod properties for modification is unsupported"); + } + + Z_OBJPROP_P(object); /* build properties hash table */ + + zv = std_object_handlers.read_property(object, member, type, key TSRMLS_CC); + if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJ_HANDLER_P(zv, clone_obj)) { + /* defensive copy */ + zend_object_value zov = Z_OBJ_HANDLER_P(zv, clone_obj)(zv TSRMLS_CC); + MAKE_STD_ZVAL(zv); + Z_TYPE_P(zv) = IS_OBJECT; + Z_OBJVAL_P(zv) = zov; + } + + return zv; +} +/* }}} */ + +/* {{{ date_period_write_property */ +static void date_period_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC) +{ + php_error_docref(NULL TSRMLS_CC, E_ERROR, "Writing to DatePeriod properties is unsupported"); +} +/* }}} */ + + /* * Local variables: * tab-width: 4 diff --git a/ext/date/php_date.h b/ext/date/php_date.h index 3af3fa42e..efae0a1db 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -101,6 +101,8 @@ PHP_FUNCTION(date_interval_format); PHP_FUNCTION(date_interval_create_from_date_string); PHP_METHOD(DatePeriod, __construct); +PHP_METHOD(DatePeriod, __wakeup); +PHP_METHOD(DatePeriod, __set_state); /* Options and Configuration */ PHP_FUNCTION(date_default_timezone_set); diff --git a/ext/date/tests/bug45682.phpt b/ext/date/tests/bug45682.phpt index d8bbfc5a0..094c7fdf4 100644 --- a/ext/date/tests/bug45682.phpt +++ b/ext/date/tests/bug45682.phpt @@ -11,8 +11,8 @@ $other = new DateTime("31-July-2008"); $diff = date_diff($date, $other); var_dump($diff); ---EXPECT-- -object(DateInterval)#3 (8) { +--EXPECTF-- +object(DateInterval)#%d (15) { ["y"]=> int(0) ["m"]=> @@ -25,8 +25,22 @@ object(DateInterval)#3 (8) { int(0) ["s"]=> int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) ["invert"]=> int(0) ["days"]=> - int(3) + string(1) "3" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) } diff --git a/ext/date/tests/bug48678.phpt b/ext/date/tests/bug48678.phpt index e2cb724f7..253cb84ce 100644 --- a/ext/date/tests/bug48678.phpt +++ b/ext/date/tests/bug48678.phpt @@ -15,8 +15,15 @@ DateInterval Object [h] => 12 [i] => 30 [s] => 5 + [weekday] => 0 + [weekday_behavior] => 0 + [first_last_day_of] => 0 [invert] => 0 - [days] =>%s + [days] => + [special_type] => 0 + [special_amount] => 0 + [have_weekday_relative] => 0 + [have_special_relative] => 0 ) DateInterval Object ( @@ -26,6 +33,13 @@ DateInterval Object [h] => 12 [i] => 30 [s] => 5 + [weekday] => 0 + [weekday_behavior] => 0 + [first_last_day_of] => 0 [invert] => 0 - [days] =>%s + [days] => 0 + [special_type] => 0 + [special_amount] => 0 + [have_weekday_relative] => 0 + [have_special_relative] => 0 ) diff --git a/ext/date/tests/bug49081.phpt b/ext/date/tests/bug49081.phpt index f4f02903d..31f735148 100644 --- a/ext/date/tests/bug49081.phpt +++ b/ext/date/tests/bug49081.phpt @@ -17,6 +17,13 @@ DateInterval Object [h] => 4 [i] => 0 [s] => 0 + [weekday] => 0 + [weekday_behavior] => 0 + [first_last_day_of] => 0 [invert] => 0 [days] => 30 + [special_type] => 0 + [special_amount] => 0 + [have_weekday_relative] => 0 + [have_special_relative] => 0 ) diff --git a/ext/date/tests/bug49778.phpt b/ext/date/tests/bug49778.phpt index 67c8e27f9..cc52a23b2 100644 --- a/ext/date/tests/bug49778.phpt +++ b/ext/date/tests/bug49778.phpt @@ -8,7 +8,7 @@ echo $i->format("%d"), "\n"; echo $i->format("%a"), "\n"; ?> --EXPECT-- -object(DateInterval)#1 (8) { +object(DateInterval)#1 (15) { ["y"]=> int(0) ["m"]=> @@ -21,10 +21,24 @@ object(DateInterval)#1 (8) { int(0) ["s"]=> int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) ["invert"]=> int(0) ["days"]=> bool(false) + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) } 7 (unknown) diff --git a/ext/date/tests/bug52113.phpt b/ext/date/tests/bug52113.phpt index a7d9339d1..862e92e96 100644 --- a/ext/date/tests/bug52113.phpt +++ b/ext/date/tests/bug52113.phpt @@ -32,7 +32,7 @@ var_dump($unser, $p); ?> --EXPECT-- -object(DateInterval)#3 (8) { +object(DateInterval)#3 (15) { ["y"]=> int(0) ["m"]=> @@ -45,12 +45,26 @@ object(DateInterval)#3 (8) { int(0) ["s"]=> int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) ["invert"]=> int(0) ["days"]=> + string(1) "0" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> int(0) } -string(128) "O:12:"DateInterval":8:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:0;s:1:"h";i:4;s:1:"i";i:0;s:1:"s";i:0;s:6:"invert";i:0;s:4:"days";i:0;}" +string(328) "O:12:"DateInterval":15:{s:1:"y";i:0;s:1:"m";i:0;s:1:"d";i:0;s:1:"h";i:4;s:1:"i";i:0;s:1:"s";i:0;s:7:"weekday";i:0;s:16:"weekday_behavior";i:0;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";s:1:"0";s:12:"special_type";i:0;s:14:"special_amount";s:1:"0";s:21:"have_weekday_relative";i:0;s:21:"have_special_relative";i:0;}" DateInterval::__set_state(array( 'y' => 0, 'm' => 0, @@ -58,9 +72,16 @@ DateInterval::__set_state(array( 'h' => 4, 'i' => 0, 's' => 0, + 'weekday' => 0, + 'weekday_behavior' => 0, + 'first_last_day_of' => 0, 'invert' => 0, - 'days' => 0, -))object(DateInterval)#5 (8) { + 'days' => '0', + 'special_type' => 0, + 'special_amount' => '0', + 'have_weekday_relative' => 0, + 'have_special_relative' => 0, +))object(DateInterval)#5 (15) { ["y"]=> int(0) ["m"]=> @@ -73,14 +94,78 @@ DateInterval::__set_state(array( int(0) ["s"]=> int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) ["invert"]=> int(0) ["days"]=> + string(1) "0" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> int(0) } -object(DatePeriod)#6 (0) { +object(DatePeriod)#6 (6) { + ["start"]=> + object(DateTime)#4 (3) { + ["date"]=> + string(19) "2003-01-02 08:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["current"]=> + NULL + ["end"]=> + NULL + ["interval"]=> + object(DateInterval)#7 (15) { + ["y"]=> + int(0) + ["m"]=> + int(0) + ["d"]=> + int(0) + ["h"]=> + int(4) + ["i"]=> + int(0) + ["s"]=> + int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) + ["invert"]=> + int(0) + ["days"]=> + string(1) "0" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) + } + ["recurrences"]=> + int(3) + ["include_start_date"]=> + bool(true) } -object(DateInterval)#4 (8) { +object(DateInterval)#8 (15) { ["y"]=> int(7) ["m"]=> @@ -93,10 +178,74 @@ object(DateInterval)#4 (8) { int(3) ["s"]=> int(2) + ["weekday"]=> + int(-1) + ["weekday_behavior"]=> + int(-1) + ["first_last_day_of"]=> + int(-1) ["invert"]=> int(1) ["days"]=> - int(2400) + string(4) "2400" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(2) "-1" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) } -object(DatePeriod)#7 (0) { +object(DatePeriod)#9 (6) { + ["start"]=> + object(DateTime)#6 (3) { + ["date"]=> + string(19) "2003-01-02 08:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["current"]=> + NULL + ["end"]=> + NULL + ["interval"]=> + object(DateInterval)#7 (15) { + ["y"]=> + int(0) + ["m"]=> + int(0) + ["d"]=> + int(0) + ["h"]=> + int(4) + ["i"]=> + int(0) + ["s"]=> + int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) + ["invert"]=> + int(0) + ["days"]=> + string(1) "0" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) + } + ["recurrences"]=> + int(3) + ["include_start_date"]=> + bool(true) } diff --git a/ext/date/tests/bug52738.phpt b/ext/date/tests/bug52738.phpt index fc1b6029e..ea219f7c7 100644 --- a/ext/date/tests/bug52738.phpt +++ b/ext/date/tests/bug52738.phpt @@ -27,6 +27,13 @@ di Object [h] => 0 [i] => 0 [s] => 0 + [weekday] => 0 + [weekday_behavior] => 0 + [first_last_day_of] => 0 [invert] => 0 [days] => + [special_type] => 0 + [special_amount] => 0 + [have_weekday_relative] => 0 + [have_special_relative] => 0 ) diff --git a/ext/date/tests/bug52808.phpt b/ext/date/tests/bug52808.phpt index e031ac6ee..e3b38bb5f 100644 --- a/ext/date/tests/bug52808.phpt +++ b/ext/date/tests/bug52808.phpt @@ -25,7 +25,7 @@ foreach($intervals as $iv) { echo "==DONE==\n"; ?> --EXPECTF-- -object(DateInterval)#%d (8) { +object(DateInterval)#%d (15) { ["y"]=> int(1) ["m"]=> @@ -38,12 +38,26 @@ object(DateInterval)#%d (8) { int(30) ["s"]=> int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) ["invert"]=> int(1) ["days"]=> - int(437) + string(3) "437" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) } -object(DateInterval)#%d (8) { +object(DateInterval)#%d (15) { ["y"]=> int(0) ["m"]=> @@ -56,12 +70,26 @@ object(DateInterval)#%d (8) { int(30) ["s"]=> int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) ["invert"]=> int(0) ["days"]=> - int(294) + string(3) "294" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) } -object(DateInterval)#%d (8) { +object(DateInterval)#%d (15) { ["y"]=> int(0) ["m"]=> @@ -74,10 +102,24 @@ object(DateInterval)#%d (8) { int(30) ["s"]=> int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) ["invert"]=> int(0) ["days"]=> - int(294) + string(3) "294" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) } DateInterval::__construct(): Failed to parse interval (2007-05-11T15:30:00Z/) DateInterval::__construct(): Failed to parse interval (2007-05-11T15:30:00Z) diff --git a/ext/date/tests/bug53437.phpt b/ext/date/tests/bug53437.phpt index f08986653..7a282ab6c 100644 --- a/ext/date/tests/bug53437.phpt +++ b/ext/date/tests/bug53437.phpt @@ -1,7 +1,5 @@ --TEST-- -Bug #53437 (Crash when using unserialized DatePeriod instance) ---XFAIL-- -Bug #53437 Not fixed yet +Bug #53437 (Crash when using unserialized DatePeriod instance), variation 1 --FILE-- <?php $dp = new DatePeriod(new DateTime('2010-01-01 UTC'), new DateInterval('P1D'), 2); @@ -20,9 +18,137 @@ $dpu = unserialize($ser); // $dpu has invalid values??? var_dump($dpu); echo "Unserialized:\r\n"; -// ???which leads to CRASH: foreach($dpu as $dt) { echo $dt->format('Y-m-d H:i:s')."\r\n"; } ?> +==DONE== --EXPECT-- +Original: +2010-01-01 00:00:00 +2010-01-02 00:00:00 +2010-01-03 00:00:00 + +object(DatePeriod)#1 (6) { + ["start"]=> + object(DateTime)#2 (3) { + ["date"]=> + string(19) "2010-01-01 00:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["current"]=> + object(DateTime)#4 (3) { + ["date"]=> + string(19) "2010-01-04 00:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["end"]=> + NULL + ["interval"]=> + object(DateInterval)#5 (15) { + ["y"]=> + int(0) + ["m"]=> + int(0) + ["d"]=> + int(1) + ["h"]=> + int(0) + ["i"]=> + int(0) + ["s"]=> + int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) + ["invert"]=> + int(0) + ["days"]=> + bool(false) + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) + } + ["recurrences"]=> + int(3) + ["include_start_date"]=> + bool(true) +} +object(DatePeriod)#5 (6) { + ["start"]=> + object(DateTime)#10 (3) { + ["date"]=> + string(19) "2010-01-01 00:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["current"]=> + object(DateTime)#7 (3) { + ["date"]=> + string(19) "2010-01-04 00:00:00" + ["timezone_type"]=> + int(3) + ["timezone"]=> + string(3) "UTC" + } + ["end"]=> + NULL + ["interval"]=> + object(DateInterval)#8 (15) { + ["y"]=> + int(0) + ["m"]=> + int(0) + ["d"]=> + int(1) + ["h"]=> + int(0) + ["i"]=> + int(0) + ["s"]=> + int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) + ["invert"]=> + int(0) + ["days"]=> + string(1) "0" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) + } + ["recurrences"]=> + int(3) + ["include_start_date"]=> + bool(true) +} +Unserialized: +2010-01-01 00:00:00 +2010-01-02 00:00:00 +2010-01-03 00:00:00 +==DONE== diff --git a/ext/date/tests/bug53437_var1.phpt b/ext/date/tests/bug53437_var1.phpt new file mode 100644 index 000000000..f1f9843d5 --- /dev/null +++ b/ext/date/tests/bug53437_var1.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug #53437 (Crash when using unserialized DatePeriod instance), variation 2 +--FILE-- +<?php +$s = 'O:10:"DatePeriod":0:{}'; + +$dp = unserialize($s); + +var_dump($dp); +?> +==DONE== +--EXPECTF-- +Fatal error: Invalid serialization data for DatePeriod object in %sbug53437_var1.php on line %d diff --git a/ext/date/tests/bug53437_var2.phpt b/ext/date/tests/bug53437_var2.phpt new file mode 100644 index 000000000..70565960e --- /dev/null +++ b/ext/date/tests/bug53437_var2.phpt @@ -0,0 +1,80 @@ +--TEST-- +Bug #53437 DateInterval basic serialization +--FILE-- +<?php +$di0 = new DateInterval('P2Y4DT6H8M'); + +$s = serialize($di0); + +$di1 = unserialize($s); + +var_dump($di0, $di1); + +?> +==DONE== +--EXPECT-- +object(DateInterval)#1 (15) { + ["y"]=> + int(2) + ["m"]=> + int(0) + ["d"]=> + int(4) + ["h"]=> + int(6) + ["i"]=> + int(8) + ["s"]=> + int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) + ["invert"]=> + int(0) + ["days"]=> + bool(false) + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) +} +object(DateInterval)#2 (15) { + ["y"]=> + int(2) + ["m"]=> + int(0) + ["d"]=> + int(4) + ["h"]=> + int(6) + ["i"]=> + int(8) + ["s"]=> + int(0) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) + ["invert"]=> + int(0) + ["days"]=> + string(1) "0" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) +} +==DONE== diff --git a/ext/date/tests/bug53437_var3.phpt b/ext/date/tests/bug53437_var3.phpt new file mode 100644 index 000000000..06f68df61 --- /dev/null +++ b/ext/date/tests/bug53437_var3.phpt @@ -0,0 +1,45 @@ +--TEST-- +Bug #53437 DateInterval unserialize bad data +--FILE-- +<?php +$s = 'O:12:"DateInterval":15:{s:1:"y";s:1:"2";s:1:"m";s:1:"0";s:1:"d";s:3:"bla";s:1:"h";s:1:"6";s:1:"i";s:1:"8";s:1:"s";s:1:"0";s:7:"weekday";i:10;s:16:"weekday_behavior";i:10;s:17:"first_last_day_of";i:0;s:6:"invert";i:0;s:4:"days";s:4:"aoeu";s:12:"special_type";i:0;s:14:"special_amount";s:21:"234523452345234532455";s:21:"have_weekday_relative";i:21474836489;s:21:"have_special_relative";s:3:"bla";}'; + +$di = unserialize($s); +var_dump($di); + +?> +==DONE== +--EXPECT-- +object(DateInterval)#1 (15) { + ["y"]=> + int(2) + ["m"]=> + int(0) + ["d"]=> + int(0) + ["h"]=> + int(6) + ["i"]=> + int(8) + ["s"]=> + int(0) + ["weekday"]=> + int(10) + ["weekday_behavior"]=> + int(10) + ["first_last_day_of"]=> + int(0) + ["invert"]=> + int(0) + ["days"]=> + string(1) "0" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(19) "9223372036854775807" + ["have_weekday_relative"]=> + int(9) + ["have_special_relative"]=> + int(0) +} +==DONE== diff --git a/ext/date/tests/bug55397.phpt b/ext/date/tests/bug55397.phpt index efc09b504..13778a00b 100644 --- a/ext/date/tests/bug55397.phpt +++ b/ext/date/tests/bug55397.phpt @@ -7,5 +7,4 @@ date_default_timezone_set('Europe/Prague'); var_dump(unserialize('O:8:"DateTime":0:{}') == new DateTime); ?> --EXPECTF-- -Warning: %s: Trying to compare an incomplete DateTime object in %s on line %d -bool(false) +Fatal error: Invalid serialization data for DateTime object in %sbug55397.php on line %d diff --git a/ext/date/tests/bug62852.phpt b/ext/date/tests/bug62852.phpt index 26de51021..7013a3f97 100644 --- a/ext/date/tests/bug62852.phpt +++ b/ext/date/tests/bug62852.phpt @@ -1,36 +1,14 @@ --TEST-- -Bug #62852 (Unserialize invalid DateTime causes crash) +Bug #62852 (Unserialize invalid DateTime causes crash), variation 1 --INI-- date.timezone=GMT ---XFAIL-- -bug is not fixed yet --FILE-- <?php $s1 = 'O:8:"DateTime":3:{s:4:"date";s:20:"10007-06-07 03:51:49";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}'; -$s2 = 'O:3:"Foo":3:{s:4:"date";s:20:"10007-06-07 03:51:49";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}'; -global $foo; - -class Foo extends DateTime { - function __wakeup() { - global $foo; - $foo = $this; - parent::__wakeup(); - } -} - -// Old test case try { unserialize( $s1 ); } catch ( Exception $e ) {} -// My test case -try { - unserialize( $s2 ); -} catch ( Exception $e ) {} -var_dump( $foo ); - -echo "okey"; -?> --EXPECTF-- -okey +Fatal error: Invalid serialization data for DateTime object in %sbug62852.php on line %d diff --git a/ext/date/tests/bug62852_var2.phpt b/ext/date/tests/bug62852_var2.phpt new file mode 100644 index 000000000..f93ba28ab --- /dev/null +++ b/ext/date/tests/bug62852_var2.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #62852 (Unserialize invalid DateTime causes crash), variation 2 +--INI-- +date.timezone=GMT +--FILE-- +<?php +$s2 = 'O:3:"Foo":3:{s:4:"date";s:20:"10007-06-07 03:51:49";s:13:"timezone_type";i:3;s:8:"timezone";s:3:"UTC";}'; + +global $foo; + +class Foo extends DateTime { + function __wakeup() { + global $foo; + $foo = $this; + parent::__wakeup(); + } +} + +try { + unserialize( $s2 ); +} catch ( Exception $e ) {} +var_dump( $foo ); + +--EXPECTF-- +Fatal error: Invalid serialization data for DateTime object in %sbug62852_var2.php on line %d diff --git a/ext/date/tests/bug62852_var3.phpt b/ext/date/tests/bug62852_var3.phpt new file mode 100644 index 000000000..5a644b547 --- /dev/null +++ b/ext/date/tests/bug62852_var3.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #62852 (Unserialize invalid DateTime causes crash), variation 3 +--INI-- +date.timezone=GMT +--FILE-- +<?php +$s2 = 'O:3:"Foo":3:{s:4:"date";s:19:"0000-00-00 00:00:00";s:13:"timezone_type";i:0;s:8:"timezone";s:3:"UTC";}'; + +global $foo; + +class Foo extends DateTime { + function __wakeup() { + global $foo; + $foo = $this; + parent::__wakeup(); + } +} + +try { + unserialize( $s2 ); +} catch ( Exception $e ) {} +var_dump( $foo ); + +--EXPECTF-- +Fatal error: Invalid serialization data for DateTime object in %sbug62852_var3.php on line %d diff --git a/ext/date/tests/date_diff1.phpt b/ext/date/tests/date_diff1.phpt index cf32fcbf3..3f3d1da7a 100644 --- a/ext/date/tests/date_diff1.phpt +++ b/ext/date/tests/date_diff1.phpt @@ -28,7 +28,7 @@ object(DateTime)#2 (3) { ["timezone"]=> string(3) "EDT" } -object(DateInterval)#3 (8) { +object(DateInterval)#3 (15) { ["y"]=> int(0) ["m"]=> @@ -41,8 +41,22 @@ object(DateInterval)#3 (8) { int(19) ["s"]=> int(40) + ["weekday"]=> + int(0) + ["weekday_behavior"]=> + int(0) + ["first_last_day_of"]=> + int(0) ["invert"]=> int(0) ["days"]=> - int(33) + string(2) "33" + ["special_type"]=> + int(0) + ["special_amount"]=> + string(1) "0" + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) } diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c index f4183d2f9..6c8cf84e9 100644 --- a/ext/dom/dom_iterators.c +++ b/ext/dom/dom_iterators.c @@ -157,35 +157,22 @@ static void php_dom_iterator_current_data(zend_object_iterator *iter, zval ***da } /* }}} */ -static int php_dom_iterator_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */ +static void php_dom_iterator_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) /* {{{ */ { - zval *curobj; - xmlNodePtr curnode = NULL; - dom_object *intern; - zval *object; - int namelen; - php_dom_iterator *iterator = (php_dom_iterator *)iter; - - object = (zval *)iterator->intern.data; + zval *object = (zval *)iterator->intern.data; if (instanceof_function(Z_OBJCE_P(object), dom_nodelist_class_entry TSRMLS_CC)) { - *int_key = iter->index; - return HASH_KEY_IS_LONG; + ZVAL_LONG(key, iter->index); } else { - curobj = iterator->curobj; + dom_object *intern = (dom_object *)zend_object_store_get_object(iterator->curobj TSRMLS_CC); - intern = (dom_object *)zend_object_store_get_object(curobj TSRMLS_CC); if (intern != NULL && intern->ptr != NULL) { - curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node; + xmlNodePtr curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->ptr)->node; + ZVAL_STRINGL(key, (char *) curnode->name, xmlStrlen(curnode->name), 1); } else { - return HASH_KEY_NON_EXISTANT; + ZVAL_NULL(key); } - - namelen = xmlStrlen(curnode->name); - *str_key = estrndup(curnode->name, namelen); - *str_key_len = namelen + 1; - return HASH_KEY_IS_STRING; } } /* }}} */ diff --git a/ext/intl/breakiterator/breakiterator_iterators.cpp b/ext/intl/breakiterator/breakiterator_iterators.cpp index d88ad8a71..3748991ae 100644 --- a/ext/intl/breakiterator/breakiterator_iterators.cpp +++ b/ext/intl/breakiterator/breakiterator_iterators.cpp @@ -139,14 +139,10 @@ static void _breakiterator_parts_destroy_it(zend_object_iterator *iter TSRMLS_DC zval_ptr_dtor(reinterpret_cast<zval**>(&iter->data)); } -static int _breakiterator_parts_get_current_key(zend_object_iterator *iter, - char **str_key, - uint *str_key_len, - ulong *int_key TSRMLS_DC) +static void _breakiterator_parts_get_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) { /* the actual work is done in move_forward and rewind */ - *int_key = iter->index; - return HASH_KEY_IS_LONG; + ZVAL_LONG(key, iter->index); } static void _breakiterator_parts_move_forward(zend_object_iterator *iter TSRMLS_DC) @@ -343,4 +339,4 @@ U_CFUNC void breakiterator_register_IntlPartsIterator_class(TSRMLS_D) PARTSITER_DECL_LONG_CONST(KEY_RIGHT); #undef PARTSITER_DECL_LONG_CONST -}
\ No newline at end of file +} diff --git a/ext/intl/common/common_enum.cpp b/ext/intl/common/common_enum.cpp index da47a437a..3ba785582 100644 --- a/ext/intl/common/common_enum.cpp +++ b/ext/intl/common/common_enum.cpp @@ -251,19 +251,7 @@ static PHP_METHOD(IntlIterator, key) INTLITERATOR_METHOD_FETCH_OBJECT; if (ii->iterator->funcs->get_current_key) { - char *str_key; - uint str_key_len; - ulong int_key; - - switch (ii->iterator->funcs->get_current_key( - ii->iterator, &str_key, &str_key_len, &int_key TSRMLS_CC)) { - case HASH_KEY_IS_LONG: - RETURN_LONG(int_key); - break; - case HASH_KEY_IS_STRING: - RETURN_STRINGL(str_key, str_key_len-1, 0); - break; - } + ii->iterator->funcs->get_current_key(ii->iterator, return_value TSRMLS_CC); } else { RETURN_LONG(ii->iterator->index); } diff --git a/ext/intl/resourcebundle/resourcebundle_iterator.c b/ext/intl/resourcebundle/resourcebundle_iterator.c index 16e1b9287..78236fda5 100644 --- a/ext/intl/resourcebundle/resourcebundle_iterator.c +++ b/ext/intl/resourcebundle/resourcebundle_iterator.c @@ -101,21 +101,18 @@ static void resourcebundle_iterator_current( zend_object_iterator *iter, zval ** /* }}} */ /* {{{ resourcebundle_iterator_key */ -static int resourcebundle_iterator_key( zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC ) +static void resourcebundle_iterator_key( zend_object_iterator *iter, zval *key TSRMLS_DC ) { ResourceBundle_iterator *iterator = (ResourceBundle_iterator *) iter; if (!iterator->current) { resourcebundle_iterator_read( iterator TSRMLS_CC); } + if (iterator->is_table) { - *str_key = estrdup( iterator->currentkey ); - *str_key_len = strlen( iterator->currentkey ) + 1; - return HASH_KEY_IS_STRING; - } - else { - *int_key = iterator->i; - return HASH_KEY_IS_LONG; + ZVAL_STRING(key, iterator->currentkey, 1); + } else { + ZVAL_LONG(key, iterator->i); } } /* }}} */ diff --git a/ext/mysqli/mysqli_result_iterator.c b/ext/mysqli/mysqli_result_iterator.c index 0f5ccdd63..3ea7bafe4 100644 --- a/ext/mysqli/mysqli_result_iterator.c +++ b/ext/mysqli/mysqli_result_iterator.c @@ -150,12 +150,11 @@ static void php_mysqli_result_iterator_rewind(zend_object_iterator *iter TSRMLS_ /* {{{ php_mysqli_result_iterator_current_key */ -static int php_mysqli_result_iterator_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) +static void php_mysqli_result_iterator_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) { php_mysqli_result_iterator *iterator = (php_mysqli_result_iterator*) iter; - *int_key = (ulong) iterator->row_num; - return HASH_KEY_IS_LONG; + ZVAL_LONG(key, iterator->row_num); } /* }}} */ diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index 4916c0650..15b293825 100644 --- a/ext/mysqlnd/mysqlnd_ps.c +++ b/ext/mysqlnd/mysqlnd_ps.c @@ -1463,7 +1463,7 @@ MYSQLND_METHOD(mysqlnd_stmt, bind_one_parameter)(MYSQLND_STMT * const s, unsigne if (stmt->param_count) { if (!stmt->param_bind) { - stmt->param_bind = mnd_ecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND)); + stmt->param_bind = mnd_pecalloc(stmt->param_count, sizeof(MYSQLND_PARAM_BIND), stmt->persistent); if (!stmt->param_bind) { DBG_RETURN(FAIL); } diff --git a/ext/oci8/php_oci8_int.h b/ext/oci8/php_oci8_int.h index d8d7d0f47..155e57d2c 100644 --- a/ext/oci8/php_oci8_int.h +++ b/ext/oci8/php_oci8_int.h @@ -191,7 +191,7 @@ typedef struct { /* php_oci_statement {{{ */ sword errcode; /* last errcode*/ OCIError *err; /* private error handle */ OCIStmt *stmt; /* statement handle */ - char *last_query; /* last query issued. also used to determine if this is a statement or a refcursor recieved from Oracle */ + char *last_query; /* last query issued. also used to determine if this is a statement or a refcursor received from Oracle */ long last_query_len; /* last query length */ HashTable *columns; /* hash containing all the result columns */ HashTable *binds; /* binds hash */ diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c new file mode 100644 index 000000000..d4299c8ad --- /dev/null +++ b/ext/opcache/Optimizer/block_pass.c @@ -0,0 +1,2028 @@ +#define DEBUG_BLOCKPASS 0 + +/* Checks if a constant (like "true") may be replaced by its value */ +static int zend_get_persistent_constant(char *name, uint name_len, zval *result, int copy TSRMLS_DC ELS_DC) +{ + zend_constant *c; + char *lookup_name; + int retval = 1; + ALLOCA_FLAG(use_heap); + + if (zend_hash_find(EG(zend_constants), name, name_len + 1, (void **) &c) == FAILURE) { + lookup_name = DO_ALLOCA(name_len + 1); + memcpy(lookup_name, name, name_len + 1); + zend_str_tolower(lookup_name, name_len); + + if (zend_hash_find(EG(zend_constants), lookup_name, name_len + 1, (void **) &c) == SUCCESS) { + if (!(c->flags & CONST_CT_SUBST) || (c->flags & CONST_CS)) { + retval = 0; + } + } else { + retval = 0; + } + FREE_ALLOCA(lookup_name); + } + + if (retval) { + if (c->flags & CONST_PERSISTENT) { + *result = c->value; + if (copy) { + zval_copy_ctor(result); + } + } else { + retval = 0; + } + } + + return retval; +} + +#if DEBUG_BLOCKPASS +# define BLOCK_REF(b) b?op_array->opcodes-b->start_opline:-1 + +static inline void print_block(zend_code_block *block, zend_op *opcodes, char *txt) +{ + fprintf(stderr, "%sBlock: %d-%d (%d)", txt, block->start_opline - opcodes, block->start_opline - opcodes + block->len - 1, block->len); + if (!block->access) { + fprintf(stderr, " unused"); + } + if (block->op1_to) { + fprintf(stderr, " 1: %d", block->op1_to->start_opline - opcodes); + } + if (block->op2_to) { + fprintf(stderr, " 2: %d", block->op2_to->start_opline - opcodes); + } + if (block->ext_to) { + fprintf(stderr, " e: %d", block->ext_to->start_opline - opcodes); + } + if (block->follow_to) { + fprintf(stderr, " f: %d", block->follow_to->start_opline - opcodes); + } + + if (block->sources) { + zend_block_source *bs = block->sources; + fprintf(stderr, " s:"); + while (bs) { + fprintf(stderr, " %d", bs->from->start_opline - opcodes); + bs = bs->next; + } + } + + fprintf(stderr, "\n"); + fflush(stderr); +} +#else +#define print_block(a,b,c) +#endif + +#define START_BLOCK_OP(opno) blocks[opno].start_opline = &op_array->opcodes[opno]; blocks[opno].start_opline_no = opno; blocks[opno].access = 1 + +/* find code blocks in op_array + code block is a set of opcodes with single flow of control, i.e. without jmps, + branches, etc. */ +static int find_code_blocks(zend_op_array *op_array, zend_cfg *cfg) +{ + zend_op *opline; + zend_op *end = op_array->opcodes + op_array->last; + zend_code_block *blocks, *cur_block; + zend_uint opno = 0; + + memset(cfg, 0, sizeof(zend_cfg)); + blocks = cfg->blocks = ecalloc(op_array->last + 2, sizeof(zend_code_block)); + opline = op_array->opcodes; + blocks[0].start_opline = opline; + blocks[0].start_opline_no = 0; + while (opline < end) { + switch((unsigned)opline->opcode) { + case ZEND_BRK: + case ZEND_CONT: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_GOTO: +#endif + /* would not optimize non-optimized BRK/CONTs - we cannot + really know where it jumps, so these optimizations are + too dangerous */ + efree(blocks); + return 0; +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_CALL: + START_BLOCK_OP(ZEND_OP1(opline).opline_num); + break; +#endif + case ZEND_JMP: + START_BLOCK_OP(ZEND_OP1(opline).opline_num); + /* break missing intentionally */ + case ZEND_RETURN: +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_RETURN_BY_REF: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_RET: +#endif + case ZEND_EXIT: + case ZEND_THROW: + /* start new block from this+1 */ + START_BLOCK_OP(opno + 1); + break; + /* TODO: if conditional jmp depends on constant, + don't start block that won't be executed */ + case ZEND_CATCH: + START_BLOCK_OP(opline->extended_value); + START_BLOCK_OP(opno + 1); + break; + case ZEND_JMPZNZ: + START_BLOCK_OP(opline->extended_value); + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_FE_FETCH: + case ZEND_FE_RESET: + case ZEND_NEW: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_JMP_SET: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + START_BLOCK_OP(ZEND_OP2(opline).opline_num); + START_BLOCK_OP(opno + 1); + break; + + } + opno++; + opline++; + } + + /* first find block start points */ + if (op_array->last_try_catch) { + int i; + cfg->try = ecalloc(op_array->last_try_catch, sizeof(zend_code_block *)); + cfg->catch = ecalloc(op_array->last_try_catch, sizeof(zend_code_block *)); + for (i = 0; i< op_array->last_try_catch; i++) { + cfg->try[i] = &blocks[op_array->try_catch_array[i].try_op]; + cfg->catch[i] = &blocks[op_array->try_catch_array[i].catch_op]; + START_BLOCK_OP(op_array->try_catch_array[i].try_op); + START_BLOCK_OP(op_array->try_catch_array[i].catch_op); + blocks[op_array->try_catch_array[i].try_op].protected = 1; + } + } + /* Currently, we don't optimize op_arrays with BRK/CONT/GOTO opcodes, + * but, we have to keep brk_cont_array to avoid memory leaks during + * exception handling */ + if (op_array->last_brk_cont) { + int i, j; + + j = 0; + for (i = 0; i< op_array->last_brk_cont; i++) { + if (op_array->brk_cont_array[i].start >= 0 && + (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_SWITCH_FREE)) { + int parent = op_array->brk_cont_array[i].parent; + + while (parent >= 0 && + op_array->brk_cont_array[parent].start < 0 && + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_FREE && + op_array->opcodes[op_array->brk_cont_array[parent].brk].opcode != ZEND_SWITCH_FREE) { + parent = op_array->brk_cont_array[parent].parent; + } + op_array->brk_cont_array[i].parent = parent; + j++; + } + } + if (j) { + cfg->loop_start = ecalloc(op_array->last_brk_cont, sizeof(zend_code_block *)); + cfg->loop_cont = ecalloc(op_array->last_brk_cont, sizeof(zend_code_block *)); + cfg->loop_brk = ecalloc(op_array->last_brk_cont, sizeof(zend_code_block *)); + j = 0; + for (i = 0; i< op_array->last_brk_cont; i++) { + if (op_array->brk_cont_array[i].start >= 0 && + (op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_FREE || + op_array->opcodes[op_array->brk_cont_array[i].brk].opcode == ZEND_SWITCH_FREE)) { + if (i != j) { + op_array->brk_cont_array[j] = op_array->brk_cont_array[i]; + } + cfg->loop_start[j] = &blocks[op_array->brk_cont_array[j].start]; + cfg->loop_cont[j] = &blocks[op_array->brk_cont_array[j].cont]; + cfg->loop_brk[j] = &blocks[op_array->brk_cont_array[j].brk]; + START_BLOCK_OP(op_array->brk_cont_array[j].start); + START_BLOCK_OP(op_array->brk_cont_array[j].cont); + START_BLOCK_OP(op_array->brk_cont_array[j].brk); + blocks[op_array->brk_cont_array[j].start].protected = 1; + blocks[op_array->brk_cont_array[j].brk].protected = 1; + j++; + } + } + op_array->last_brk_cont = j; + } else { + efree(op_array->brk_cont_array); + op_array->brk_cont_array = NULL; + op_array->last_brk_cont = 0; + } + } + + /* Build CFG (Control Flow Graph) */ + cur_block = blocks; + for (opno = 1; opno < op_array->last; opno++) { + if (blocks[opno].start_opline) { + /* found new block start */ + cur_block->len = blocks[opno].start_opline - cur_block->start_opline; + cur_block->next = &blocks[opno]; + /* what is the last OP of previous block? */ + opline = blocks[opno].start_opline - 1; + switch((unsigned)opline->opcode) { + case ZEND_RETURN: +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_RETURN_BY_REF: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_RET: +#endif + case ZEND_EXIT: + case ZEND_THROW: + break; +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_CALL: +#endif + case ZEND_JMP: + cur_block->op1_to = &blocks[ZEND_OP1(opline).opline_num]; + break; + case ZEND_JMPZNZ: + cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num]; + cur_block->ext_to = &blocks[opline->extended_value]; + break; + case ZEND_CATCH: + cur_block->ext_to = &blocks[opline->extended_value]; + cur_block->follow_to = &blocks[opno]; + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_FE_RESET: + case ZEND_NEW: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_JMP_SET: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + case ZEND_FE_FETCH: + cur_block->op2_to = &blocks[ZEND_OP2(opline).opline_num]; + /* break missing intentionally */ + default: + /* next block follows this */ + cur_block->follow_to = &blocks[opno]; + break; + } + print_block(cur_block, op_array->opcodes, ""); + cur_block = cur_block->next; + } + } + cur_block->len = end - cur_block->start_opline; + cur_block->next = &blocks[op_array->last + 1]; + print_block(cur_block, op_array->opcodes, ""); + + return 1; +} + +/* CFG back references management */ + +#define ADD_SOURCE(fromb, tob) { \ + zend_block_source *__s = tob->sources; \ + while (__s && __s->from != fromb) __s = __s->next; \ + if (__s == NULL) { \ + zend_block_source *__t = emalloc(sizeof(zend_block_source)); \ + __t->next = tob->sources; \ + tob->sources = __t; \ + __t->from = fromb; \ + } \ +} + +#define DEL_SOURCE(cs) { \ + zend_block_source *__ns = (*cs)->next; \ + efree(*cs); \ + *cs = __ns; \ +} + + +static inline void replace_source(zend_block_source *list, zend_code_block *old, zend_code_block *new) +{ + /* replace all references to 'old' in 'list' with 'new' */ + zend_block_source **cs; + int found = 0; + + for (cs = &list; *cs; cs = &((*cs)->next)) { + if ((*cs)->from == new) { + if (found) { + DEL_SOURCE(cs); + } else { + found = 1; + } + } + + if ((*cs)->from == old) { + if (found) { + DEL_SOURCE(cs); + } else { + (*cs)->from = new; + found = 1; + } + } + } +} + +static inline void del_source(zend_code_block *from, zend_code_block *to) +{ + /* delete source 'from' from 'to'-s sources list */ + zend_block_source **cs = &to->sources; + + if (to->sources == NULL) { + to->access = 0; + return; + } + + while (*cs) { + if ((*cs)->from == from) { + DEL_SOURCE(cs); + break; + } + cs = &((*cs)->next); + } + + if (to->sources == NULL) { + /* 'to' has no more sources - it's unused, will be stripped */ + to->access = 0; + return; + } + + if (to->sources->next == NULL) { + /* source to only one block */ + zend_code_block *from_block = to->sources->from; + + if (from_block->access && from_block->follow_to == to && + from_block->op1_to == NULL && + from_block->op2_to == NULL && + from_block->ext_to == NULL) { + /* this block follows it's only predecessor - we can join them */ + zend_op *new_to = from_block->start_opline + from_block->len; + if (new_to != to->start_opline) { + /* move block to new location */ + memmove(new_to, to->start_opline, sizeof(zend_op)*to->len); + } + /* join blocks' lengths */ + from_block->len += to->len; + /* move 'to'`s references to 'from' */ + to->start_opline = NULL; + to->access = 0; + efree(to->sources); + to->sources = NULL; + from_block->follow_to = to->follow_to; + if (to->op1_to) { + from_block->op1_to = to->op1_to; + replace_source(to->op1_to->sources, to, from_block); + } + if (to->op2_to) { + from_block->op2_to = to->op2_to; + replace_source(to->op2_to->sources, to, from_block); + } + if (to->ext_to) { + from_block->ext_to = to->ext_to; + replace_source(to->ext_to->sources, to, from_block); + } + if (to->follow_to) { + replace_source(to->follow_to->sources, to, from_block); + } + /* remove "to" from list */ + } + } +} + +static void delete_code_block(zend_code_block *block) +{ + if (block->protected) { + return; + } + if (block->follow_to) { + zend_block_source *bs = block->sources; + while (bs) { + zend_code_block *from_block = bs->from; + zend_code_block *to = block->follow_to; + if (from_block->op1_to == block) { + from_block->op1_to = to; + ADD_SOURCE(from_block, to); + } + if (from_block->op2_to == block) { + from_block->op2_to = to; + ADD_SOURCE(from_block, to); + } + if (from_block->ext_to == block) { + from_block->ext_to = to; + ADD_SOURCE(from_block, to); + } + if (from_block->follow_to == block) { + from_block->follow_to = to; + ADD_SOURCE(from_block, to); + } + bs = bs->next; + } + } + block->access = 0; +} + +static void zend_access_path(zend_code_block *block) +{ + if (block->access) { + return; + } + + block->access = 1; + if (block->op1_to) { + zend_access_path(block->op1_to); + ADD_SOURCE(block, block->op1_to); + } + if (block->op2_to) { + zend_access_path(block->op2_to); + ADD_SOURCE(block, block->op2_to); + } + if (block->ext_to) { + zend_access_path(block->ext_to); + ADD_SOURCE(block, block->ext_to); + } + if (block->follow_to) { + zend_access_path(block->follow_to); + ADD_SOURCE(block, block->follow_to); + } +} + +/* Traverse CFG, mark reachable basic blocks and build back references */ +static void zend_rebuild_access_path(zend_cfg *cfg, zend_op_array *op_array, int find_start) +{ + zend_code_block *blocks = cfg->blocks; + zend_code_block *start = find_start? NULL : blocks; + zend_code_block *b; + + /* Mark all blocks as unaccessible and destroy back references */ + b = blocks; + while (b != NULL) { + zend_block_source *cs; + if (!start && b->access) { + start = b; + } + b->access = 0; + cs = b->sources; + while (cs) { + zend_block_source *n = cs->next; + efree(cs); + cs = n; + } + b->sources = NULL; + b = b->next; + } + + /* Walk thorough all paths */ + zend_access_path(start); + + /* Add brk/cont paths */ + if (op_array->last_brk_cont) { + int i; + for (i=0; i< op_array->last_brk_cont; i++) { + zend_access_path(cfg->loop_start[i]); + zend_access_path(cfg->loop_cont[i]); + zend_access_path(cfg->loop_brk[i]); + } + } + + /* Add exception paths */ + if (op_array->last_try_catch) { + int i; + for (i=0; i< op_array->last_try_catch; i++) { + if (!cfg->catch[i]->access) { + zend_access_path(cfg->catch[i]); + } + } + } +} + +/* Data dependencies macros */ + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + +# define VAR_NUM_EX(op) ((op ## _type & (IS_TMP_VAR|IS_VAR))?VAR_NUM((op).var):(op).var) + +# define VAR_SOURCE(op) Tsource[VAR_NUM(op.var)] +# define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(opline->result.var)] = opline + +# define VAR_UNSET(op) do { if (op ## _type & (IS_TMP_VAR|IS_VAR)) {VAR_SOURCE(op) = NULL;}} while (0) + +#else + +# define VAR_NUM_EX(op) ((op).op_type == IS_TMP_VAR || (op).op_type == IS_VAR? VAR_NUM((op).u.var) : (op).u.var) + +# define VAR_SOURCE(op) Tsource[VAR_NUM(op.u.var)] +# define SET_VAR_SOURCE(opline) Tsource[VAR_NUM(ZEND_RESULT(opline).var)] = opline + +# define VAR_UNSET(op) do { if ((op).op_type == IS_TMP_VAR || (op).op_type == IS_VAR) {VAR_SOURCE(op) = NULL;}} while (0) + +#endif + +#define convert_to_string_safe(v) \ + if (Z_TYPE_P((v)) == IS_NULL) { \ + ZVAL_STRINGL((v), "", 0, 1); \ + } else { \ + convert_to_string((v)); \ + } + +static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, char *used_ext TSRMLS_DC) +{ + zend_op *opline = block->start_opline; + zend_op *end, *last_op = NULL; + zend_op **Tsource = NULL; + + print_block(block, op_array->opcodes, "Opt "); + + /* remove leading NOPs */ + while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) { + if (block->len == 1) { + /* this block is all NOPs, join with following block */ + if (block->follow_to) { + delete_code_block(block); + } + return; + } + block->start_opline++; + block->start_opline_no++; + block->len--; + } + + /* we track data dependencies only insight a single basic block */ + if (op_array->T) { + Tsource = ecalloc(op_array->T, sizeof(zend_op *)); + } + opline = block->start_opline; + end = opline + block->len; + while ((op_array->T) && (opline < end)) { + /* strip X = QM_ASSIGN(const) */ + if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN && + ZEND_OP1_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && + opline->opcode != ZEND_CASE && /* CASE _always_ expects variable */ + opline->opcode != ZEND_FETCH_DIM_TMP_VAR && /* in 5.1, FETCH_DIM_TMP_VAR expects T */ + opline->opcode != ZEND_FE_RESET && + opline->opcode != ZEND_FREE + ) { + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + } + + /* T = QM_ASSIGN(C), F(T) => NOP, F(C) */ + if (ZEND_OP2_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op2) && + VAR_SOURCE(opline->op2)->opcode == ZEND_QM_ASSIGN && + ZEND_OP1_TYPE(VAR_SOURCE(opline->op2)) == IS_CONST) { + zend_op *src = VAR_SOURCE(opline->op2); + VAR_UNSET(opline->op2); + COPY_NODE(opline->op2, src->op1); + MAKE_NOP(src); + } + + /* T = PRINT(X), F(T) => ECHO(X), F(1) */ + if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_PRINT && + opline->opcode != ZEND_CASE && opline->opcode != ZEND_FREE) { + ZEND_OP1_TYPE(opline) = IS_CONST; + LITERAL_LONG(opline->op1, 1); + } + + if (ZEND_OP2_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op2) && + VAR_SOURCE(opline->op2)->opcode == ZEND_PRINT) { + ZEND_OP2_TYPE(opline) = IS_CONST; + LITERAL_LONG(opline->op2, 1); + } + + /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */ + if ((opline->opcode == ZEND_ECHO || opline->opcode == ZEND_PRINT) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_CAST && + VAR_SOURCE(opline->op1)->extended_value == IS_STRING) { + zend_op *src = VAR_SOURCE(opline->op1); + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + } + + /* T = PRINT(X), FREE(T) => ECHO(X) */ + if (opline->opcode == ZEND_FREE && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1)) { + zend_op *src = VAR_SOURCE(opline->op1); + if (src->opcode == ZEND_PRINT) { + src->opcode = ZEND_ECHO; + ZEND_RESULT_TYPE(src) = IS_UNUSED; + MAKE_NOP(opline); + } + } + + /* T = BOOL(X), FREE(T) => NOP */ + if (opline->opcode == ZEND_FREE && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1)) { + zend_op *src = VAR_SOURCE(opline->op1); + if (src->opcode == ZEND_BOOL) { + if (ZEND_OP1_TYPE(src) == IS_CONST) { + literal_dtor(&ZEND_OP1_LITERAL(src)); + } + MAKE_NOP(src); + MAKE_NOP(opline); + } + } + +#if 0 + /* pre-evaluate functions: + constant(x) + defined(x) + function_exists(x) + extension_loaded(x) + BAD: interacts badly with Accelerator + */ + if((ZEND_OP1_TYPE(opline) & IS_VAR) && + VAR_SOURCE(opline->op1) && VAR_SOURCE(opline->op1)->opcode == ZEND_DO_CF_FCALL && + VAR_SOURCE(opline->op1)->extended_value == 1) { + zend_op *fcall = VAR_SOURCE(opline->op1); + zend_op *sv = fcall-1; + if(sv >= block->start_opline && sv->opcode == ZEND_SEND_VAL && + ZEND_OP1_TYPE(sv) == IS_CONST && Z_TYPE(OPLINE_OP1_LITERAL(sv)) == IS_STRING && + Z_LVAL(OPLINE_OP2_LITERAL(sv)) == 1 + ) { + zval *arg = &OPLINE_OP1_LITERAL(sv); + char *fname = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].function_name; + int flen = FUNCTION_CACHE->funcs[Z_LVAL(ZEND_OP1_LITERAL(fcall))].name_len; + if(flen == sizeof("defined")-1 && zend_binary_strcasecmp(fname, flen, "defined", sizeof("defined")-1) == 0) { + zval c; + if(zend_get_persistent_constant(Z_STRVAL_P(arg), Z_STRLEN_P(arg), &c, 0 TSRMLS_CC ELS_CC) != 0) { + literal_dtor(arg); + MAKE_NOP(sv); + MAKE_NOP(fcall); + LITERAL_BOOL(opline->op1, 1); + ZEND_OP1_TYPE(opline) = IS_CONST; + } + } else if((flen == sizeof("function_exists")-1 && zend_binary_strcasecmp(fname, flen, "function_exists", sizeof("function_exists")-1) == 0) || + (flen == sizeof("is_callable")-1 && zend_binary_strcasecmp(fname, flen, "is_callable", sizeof("is_callable")-1) == 0) + ) { + zend_function *function; + if(zend_hash_find(EG(function_table), Z_STRVAL_P(arg), Z_STRLEN_P(arg)+1, (void **)&function) == SUCCESS) { + literal_dtor(arg); + MAKE_NOP(sv); + MAKE_NOP(fcall); + LITERAL_BOOL(opline->op1, 1); + ZEND_OP1_TYPE(opline) = IS_CONST; + } + } else if(flen == sizeof("constant")-1 && zend_binary_strcasecmp(fname, flen, "constant", sizeof("constant")-1) == 0) { + zval c; + if(zend_get_persistent_constant(Z_STRVAL_P(arg), Z_STRLEN_P(arg), &c, 1 TSRMLS_CC ELS_CC) != 0) { + literal_dtor(arg); + MAKE_NOP(sv); + MAKE_NOP(fcall); + ZEND_OP1_LITERAL(opline) = zend_optimizer_add_literal(op_array, &c TSRMLS_CC); + /* no copy ctor - get already copied it */ + ZEND_OP1_TYPE(opline) = IS_CONST; + } + } else if(flen == sizeof("extension_loaded")-1 && zend_binary_strcasecmp(fname, flen, "extension_loaded", sizeof("extension_loaded")-1) == 0) { + if(zend_hash_exists(&module_registry, Z_STRVAL_P(arg), Z_STRLEN_P(arg)+1)) { + literal_dtor(arg); + MAKE_NOP(sv); + MAKE_NOP(fcall); + LITERAL_BOOL(opline->op1, 1); + ZEND_OP1_TYPE(opline) = IS_CONST; + } + } + } + } +#endif + + /* IS_EQ(TRUE, X) => BOOL(X) + * IS_EQ(FALSE, X) => BOOL_NOT(X) + * IS_NOT_EQ(TRUE, X) => BOOL_NOT(X) + * IS_NOT_EQ(FALSE, X) => BOOL(X) + */ + if (opline->opcode == ZEND_IS_EQUAL || + opline->opcode == ZEND_IS_NOT_EQUAL) { + if (ZEND_OP1_TYPE(opline) == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_BOOL) { + opline->opcode = + ((opline->opcode == ZEND_IS_EQUAL) == Z_LVAL(ZEND_OP1_LITERAL(opline)))? + ZEND_BOOL : ZEND_BOOL_NOT; + COPY_NODE(opline->op1, opline->op2); + SET_UNUSED(opline->op2); + } else if (ZEND_OP2_TYPE(opline) == IS_CONST && + Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_BOOL) { + opline->opcode = + ((opline->opcode == ZEND_IS_EQUAL) == Z_LVAL(ZEND_OP2_LITERAL(opline)))? + ZEND_BOOL : ZEND_BOOL_NOT; + SET_UNUSED(opline->op2); + } + } + + if ((opline->opcode == ZEND_BOOL || + opline->opcode == ZEND_BOOL_NOT || + opline->opcode == ZEND_JMPZ || + opline->opcode == ZEND_JMPNZ || + opline->opcode == ZEND_JMPZNZ) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) != NULL && + !used_ext[VAR_NUM(ZEND_OP1(opline).var)] && + VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT) { + /* T = BOOL_NOT(X) + JMPZ(T) -> NOP, JMPNZ(X) */ + zend_op *src = VAR_SOURCE(opline->op1); + + COPY_NODE(opline->op1, src->op1); + + switch (opline->opcode) { + case ZEND_BOOL: + /* T = BOOL_NOT(X) + BOOL(T) -> NOP, BOOL_NOT(X) */ + opline->opcode = ZEND_BOOL_NOT; + break; + case ZEND_BOOL_NOT: + /* T = BOOL_NOT(X) + BOOL_BOOL(T) -> NOP, BOOL(X) */ + opline->opcode = ZEND_BOOL; + break; + case ZEND_JMPZ: + /* T = BOOL_NOT(X) + JMPZ(T,L) -> NOP, JMPNZ(X,L) */ + opline->opcode = ZEND_JMPNZ; + break; + case ZEND_JMPNZ: + /* T = BOOL_NOT(X) + JMPNZ(T,L) -> NOP, JMPZ(X,L) */ + opline->opcode = ZEND_JMPZ; + break; + case ZEND_JMPZNZ: + { + /* T = BOOL_NOT(X) + JMPZNZ(T,L1,L2) -> NOP, JMPZNZ(X,L2,L1) */ + int op_t; + zend_code_block *op_b; + + op_t = opline->extended_value; + opline->extended_value = ZEND_OP2(opline).opline_num; + ZEND_OP2(opline).opline_num = op_t; + + op_b = block->ext_to; + block->ext_to = block->op2_to; + block->op2_to = op_b; + } + break; + } + + VAR_UNSET(opline->op1); + MAKE_NOP(src); + continue; + } else +#if 0 + /* T = BOOL_NOT(X) + T = JMPZ_EX(T, X) -> T = BOOL_NOT(X), JMPNZ(X) */ + if(0 && (opline->opcode == ZEND_JMPZ_EX || + opline->opcode == ZEND_JMPNZ_EX) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) != NULL && + VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL_NOT && + ZEND_OP1(opline).var == ZEND_RESULT(opline).var + ) { + zend_op *src = VAR_SOURCE(opline->op1); + if(opline->opcode == ZEND_JMPZ_EX) { + opline->opcode = ZEND_JMPNZ; + } else { + opline->opcode = ZEND_JMPZ; + } + COPY_NODE(opline->op1, src->op1); + SET_UNUSED(opline->result); + continue; + } else +#endif + /* T = BOOL(X) + JMPZ(T) -> NOP, JMPZ(X) */ + if ((opline->opcode == ZEND_BOOL || + opline->opcode == ZEND_BOOL_NOT || + opline->opcode == ZEND_JMPZ || + opline->opcode == ZEND_JMPZ_EX || + opline->opcode == ZEND_JMPNZ_EX || + opline->opcode == ZEND_JMPNZ || + opline->opcode == ZEND_JMPZNZ) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) != NULL && + (!used_ext[VAR_NUM(ZEND_OP1(opline).var)] || + (ZEND_RESULT_TYPE(opline) == IS_TMP_VAR && + ZEND_RESULT(opline).var == ZEND_OP1(opline).var)) && + (VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL || + VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN)) { + zend_op *src = VAR_SOURCE(opline->op1); + COPY_NODE(opline->op1, src->op1); + + VAR_UNSET(opline->op1); + MAKE_NOP(src); + continue; + } else if (last_op && opline->opcode == ZEND_ECHO && + last_op->opcode == ZEND_ECHO && + ZEND_OP1_TYPE(opline) == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_DOUBLE && + ZEND_OP1_TYPE(last_op) == IS_CONST && + Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_DOUBLE) { + /* compress consecutive ECHO's. + * Float to string conversion may be affected by current + * locale setting. + */ + int l; + + if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP1_LITERAL(opline)); + } + if (Z_TYPE(ZEND_OP1_LITERAL(last_op)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP1_LITERAL(last_op)); + } + l = Z_STRLEN(ZEND_OP1_LITERAL(opline)) + Z_STRLEN(ZEND_OP1_LITERAL(last_op)); + if (IS_INTERNED(Z_STRVAL(ZEND_OP1_LITERAL(last_op)))) { + char *tmp = emalloc(l + 1); + memcpy(tmp, Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1); + Z_STRVAL(ZEND_OP1_LITERAL(last_op)) = tmp; + } else { + Z_STRVAL(ZEND_OP1_LITERAL(last_op)) = erealloc(Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1); + } + memcpy(Z_STRVAL(ZEND_OP1_LITERAL(last_op))+Z_STRLEN(ZEND_OP1_LITERAL(last_op)), Z_STRVAL(ZEND_OP1_LITERAL(opline)), Z_STRLEN(ZEND_OP1_LITERAL(opline))); + Z_STRVAL(ZEND_OP1_LITERAL(last_op))[l] = '\0'; + zval_dtor(&ZEND_OP1_LITERAL(opline)); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + Z_STRVAL(ZEND_OP1_LITERAL(opline)) = (char*)zend_new_interned_string(Z_STRVAL(ZEND_OP1_LITERAL(last_op)), l + 1, 1 TSRMLS_CC); + Z_TYPE(ZEND_OP1_LITERAL(last_op)) = IS_NULL; +#else + Z_STRVAL(ZEND_OP1_LITERAL(opline)) = Z_STRVAL(ZEND_OP1_LITERAL(last_op)); +#endif + Z_STRLEN(ZEND_OP1_LITERAL(opline)) = l; + MAKE_NOP(last_op); + } else if (opline->opcode == ZEND_CONCAT && + ZEND_OP2_TYPE(opline) == IS_CONST && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + (VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT || + VAR_SOURCE(opline->op1)->opcode == ZEND_ADD_STRING) && + ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && + ZEND_RESULT(VAR_SOURCE(opline->op1)).var == ZEND_OP1(opline).var) { + /* compress consecutive CONCATs */ + zend_op *src = VAR_SOURCE(opline->op1); + int l; + + if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP2_LITERAL(opline)); + } + if (Z_TYPE(ZEND_OP2_LITERAL(src)) != IS_STRING) { + convert_to_string_safe(&ZEND_OP2_LITERAL(src)); + } + + VAR_UNSET(opline->op1); + if (ZEND_OP1_TYPE(src) == IS_UNUSED) { + /* 5.3 may use IS_UNUSED as first argument to ZEND_ADD_... */ + opline->opcode = ZEND_ADD_STRING; + } + COPY_NODE(opline->op1, src->op1); + l = Z_STRLEN(ZEND_OP2_LITERAL(opline)) + Z_STRLEN(ZEND_OP2_LITERAL(src)); + if (IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(src)))) { + char *tmp = emalloc(l + 1); + memcpy(tmp, Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1); + Z_STRVAL(ZEND_OP2_LITERAL(src)) = tmp; + } else { + Z_STRVAL(ZEND_OP2_LITERAL(src)) = erealloc(Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1); + } + memcpy(Z_STRVAL(ZEND_OP2_LITERAL(src))+Z_STRLEN(ZEND_OP2_LITERAL(src)), Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline))); + Z_STRVAL(ZEND_OP2_LITERAL(src))[l] = '\0'; + if (!IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(opline)))) { + efree(Z_STRVAL(ZEND_OP2_LITERAL(opline))); + } +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + Z_STRVAL(ZEND_OP2_LITERAL(opline)) = (char*)zend_new_interned_string(Z_STRVAL(ZEND_OP2_LITERAL(src)), l + 1, 1 TSRMLS_CC); + Z_TYPE(ZEND_OP2_LITERAL(src)) = IS_NULL; +#else + Z_STRVAL(ZEND_OP2_LITERAL(opline)) = Z_STRVAL(ZEND_OP2_LITERAL(src)); +#endif + Z_STRLEN(ZEND_OP2_LITERAL(opline)) = l; + MAKE_NOP(src); + } else if ((opline->opcode == ZEND_ADD_STRING || opline->opcode == ZEND_ADD_VAR) && ZEND_OP1_TYPE(opline) == IS_CONST) { + /* convert ADD_STRING(C1, C2) to CONCAT(C1, C2) */ + opline->opcode = ZEND_CONCAT; + continue; + } else if (opline->opcode == ZEND_ADD_CHAR && ZEND_OP1_TYPE(opline) == IS_CONST && ZEND_OP2_TYPE(opline) == IS_CONST) { + /* convert ADD_CHAR(C1, C2) to CONCAT(C1, C2) */ + char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); + ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1); + opline->opcode = ZEND_CONCAT; + continue; + } else if ((opline->opcode == ZEND_ADD || + opline->opcode == ZEND_SUB || + opline->opcode == ZEND_MUL || + opline->opcode == ZEND_DIV || + opline->opcode == ZEND_MOD || + opline->opcode == ZEND_SL || + opline->opcode == ZEND_SR || + opline->opcode == ZEND_CONCAT || + opline->opcode == ZEND_IS_EQUAL || + opline->opcode == ZEND_IS_NOT_EQUAL || + opline->opcode == ZEND_IS_SMALLER || + opline->opcode == ZEND_IS_SMALLER_OR_EQUAL || + opline->opcode == ZEND_IS_IDENTICAL || + opline->opcode == ZEND_IS_NOT_IDENTICAL || + opline->opcode == ZEND_BOOL_XOR || + opline->opcode == ZEND_BW_OR || + opline->opcode == ZEND_BW_AND || + opline->opcode == ZEND_BW_XOR) && + ZEND_OP1_TYPE(opline)==IS_CONST && + ZEND_OP2_TYPE(opline)==IS_CONST) { + /* evaluate constant expressions */ + int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) = get_binary_op(opline->opcode); + zval result; + int er; + + if ((opline->opcode == ZEND_DIV || opline->opcode == ZEND_MOD) && + ((Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG && + Z_LVAL(ZEND_OP2_LITERAL(opline)) == 0) || + (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_DOUBLE && + Z_DVAL(ZEND_OP2_LITERAL(opline)) == 0.0))) { + if (RESULT_USED(opline)) { + SET_VAR_SOURCE(opline); + } + opline++; + continue; + } + er = EG(error_reporting); + EG(error_reporting) = 0; + if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline) TSRMLS_CC) == SUCCESS) { + PZ_SET_REFCOUNT_P(&result, 1); + PZ_UNSET_ISREF_P(&result); + + literal_dtor(&ZEND_OP1_LITERAL(opline)); + literal_dtor(&ZEND_OP2_LITERAL(opline)); + ZEND_OP1_LITERAL(opline) = result; + SET_UNUSED(opline->op2); + + opline->opcode = ZEND_QM_ASSIGN; + } + EG(error_reporting) = er; + } else if ((opline->opcode == ZEND_BOOL || + opline->opcode == ZEND_BOOL_NOT || + opline->opcode == ZEND_BW_NOT) && ZEND_OP1_TYPE(opline) == IS_CONST) { + /* evaluate constant unary ops */ + unary_op_type unary_op = get_unary_op(opline->opcode); + zval result; + + if (unary_op) { + unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC); + literal_dtor(&ZEND_OP1_LITERAL(opline)); + } else { + /* BOOL */ + result = ZEND_OP1_LITERAL(opline); + convert_to_boolean(&result); + } + PZ_SET_REFCOUNT_P(&result, 1); + PZ_UNSET_ISREF_P(&result); + ZEND_OP1_LITERAL(opline) = result; + opline->opcode = ZEND_QM_ASSIGN; + } else if ((opline->opcode == ZEND_RETURN || opline->opcode == ZEND_EXIT) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_QM_ASSIGN) { + /* T = QM_ASSIGN(X), RETURN(T) to RETURN(X) */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, src->op1); + MAKE_NOP(src); + } else if ((opline->opcode == ZEND_ADD_STRING || + opline->opcode == ZEND_ADD_CHAR) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_INIT_STRING) { + /* convert T = INIT_STRING(), T = ADD_STRING(T, X) to T = QM_ASSIGN(X) */ + /* CHECKME: Remove ZEND_ADD_VAR optimization, since some conversions - + namely, BOOL(false)->string - don't allocate memory but use empty_string + and ADD_CHAR fails */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, opline->op2); + if (opline->opcode == ZEND_ADD_CHAR) { + char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); + ZVAL_STRINGL(&ZEND_OP1_LITERAL(opline), &c, 1, 1); + } + SET_UNUSED(opline->op2); + MAKE_NOP(src); + opline->opcode = ZEND_QM_ASSIGN; + } else if ((opline->opcode == ZEND_ADD_STRING || + opline->opcode == ZEND_ADD_CHAR || + opline->opcode == ZEND_ADD_VAR || + opline->opcode == ZEND_CONCAT) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_CONCAT && + ZEND_OP2_TYPE(VAR_SOURCE(opline->op1)) == IS_CONST && + Z_TYPE(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == IS_STRING && + Z_STRLEN(ZEND_OP2_LITERAL(VAR_SOURCE(opline->op1))) == 0) { + /* convert T = CONCAT(X,''), T = ADD_STRING(T, Y) to T = CONCAT(X,Y) */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, src->op1); + if (opline->opcode == ZEND_ADD_CHAR) { + char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); + ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1); + } + opline->opcode = ZEND_CONCAT; + literal_dtor(&ZEND_OP2_LITERAL(src)); /* will take care of empty_string too */ + MAKE_NOP(src); + } else if (opline->opcode == ZEND_ADD_VAR && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_INIT_STRING) { + /* convert T = INIT_STRING(), T = ADD_VAR(T, X) to T = CAST(STRING, X) */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, opline->op2); + SET_UNUSED(opline->op2); + MAKE_NOP(src); + opline->opcode = ZEND_CAST; + opline->extended_value = IS_STRING; + } else if ((opline->opcode == ZEND_ADD_STRING || + opline->opcode == ZEND_ADD_CHAR || + opline->opcode == ZEND_ADD_VAR || + opline->opcode == ZEND_CONCAT) && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + VAR_SOURCE(opline->op1)->opcode == ZEND_CAST && + VAR_SOURCE(opline->op1)->extended_value == IS_STRING) { + /* convert T1 = CAST(STRING, X), T2 = CONCAT(T1, Y) to T2 = CONCAT(X,Y) */ + zend_op *src = VAR_SOURCE(opline->op1); + VAR_UNSET(opline->op1); + COPY_NODE(opline->op1, src->op1); + if (opline->opcode == ZEND_ADD_CHAR) { + char c = (char)Z_LVAL(ZEND_OP2_LITERAL(opline)); + ZVAL_STRINGL(&ZEND_OP2_LITERAL(opline), &c, 1, 1); + } + opline->opcode = ZEND_CONCAT; + MAKE_NOP(src); + } else if (opline->opcode == ZEND_QM_ASSIGN && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + ZEND_RESULT_TYPE(opline) == IS_TMP_VAR && + ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { + /* strip T = QM_ASSIGN(T) */ + MAKE_NOP(opline); + } else if (opline->opcode == ZEND_BOOL && + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + VAR_SOURCE(opline->op1) && + (VAR_SOURCE(opline->op1)->opcode == ZEND_IS_EQUAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_EQUAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_SMALLER_OR_EQUAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_BOOL || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_IDENTICAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_IS_NOT_IDENTICAL || + VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_VAR || + VAR_SOURCE(opline->op1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) && + !used_ext[VAR_NUM(ZEND_OP1(opline).var)]) { + /* T = IS_SMALLER(X, Y), T1 = BOOL(T) => T = IS_SMALLER(X, Y), T1 = QM_ASSIGN(T) */ + zend_op *src = VAR_SOURCE(opline->op1); + COPY_NODE(src->result, opline->result); + SET_VAR_SOURCE(src); + MAKE_NOP(opline); + } + /* get variable source */ + if (RESULT_USED(opline)) { + SET_VAR_SOURCE(opline); + } + if (opline->opcode != ZEND_NOP) { + last_op = opline; + } + opline++; + } + + /* remove leading NOPs */ + while (block->len > 0 && block->start_opline->opcode == ZEND_NOP) { + if (block->len == 1) { + /* this block is all NOPs, join with following block */ + if (block->follow_to) { + delete_code_block(block); + } + if (op_array->T) { + efree(Tsource); + } + return; + } + block->start_opline++; + block->start_opline_no++; + block->len--; + } + + /* strip the inside NOPs */ + opline = block->start_opline; + end = opline + block->len; + while (opline < end) { + if (opline->opcode == ZEND_NOP) { + zend_op *nop = opline + 1; + int noplen; + while (nop < end && nop->opcode == ZEND_NOP) { + nop++; + } + noplen = nop-opline; + if (nop < end) { + /* move up non-NOP opcodes */ + memmove(opline, nop, (end-nop)*sizeof(zend_op)); + } else { + /* all NOPs up to the end, do nothing */ + } + block->len -= noplen; + end = block->start_opline + block->len; + } + opline++; + } + + if (op_array->T) { + efree(Tsource); + } +} + +/* Rebuild plain (optimized) op_array from CFG */ +static void assemble_code_blocks(zend_cfg *cfg, zend_op_array *op_array) +{ + zend_code_block *blocks = cfg->blocks; + zend_op *new_opcodes = emalloc(op_array->last * sizeof(zend_op)); + zend_op *opline = new_opcodes; + zend_code_block *cur_block = blocks; + + /* Copy code of reachable blocks into a single buffer */ + while (cur_block) { + if (cur_block->access) { + memcpy(opline, cur_block->start_opline, cur_block->len * sizeof(zend_op)); + cur_block->start_opline = opline; + opline += cur_block->len; + if ((opline - 1)->opcode == ZEND_JMP) { + zend_code_block *next; + next = cur_block->next; + while (next && !next->access) { + next = next->next; + } + if (next && next == cur_block->op1_to) { + /* JMP to the next block - strip it */ + cur_block->follow_to = cur_block->op1_to; + cur_block->op1_to = NULL; + MAKE_NOP((opline - 1)); + opline--; + cur_block->len--; + } + } + } else { + /* this block will not be used, delete all constants there */ + zend_op *_opl; + zend_op *end = cur_block->start_opline + cur_block->len; + for (_opl = cur_block->start_opline; _opl && _opl < end; _opl++) { + if (ZEND_OP1_TYPE(_opl) == IS_CONST) { + literal_dtor(&ZEND_OP1_LITERAL(_opl)); + } + if (ZEND_OP2_TYPE(_opl) == IS_CONST) { + literal_dtor(&ZEND_OP2_LITERAL(_opl)); + } + } + } + cur_block = cur_block->next; + } +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if (opline[-1].opcode == ZEND_THROW) { + /* if we finished with THROW, we need to add space between THROW and HANDLE to not confuse + zend_throw_internal */ + MAKE_NOP(opline); + opline->lineno = opline[-1].lineno; + opline++; + } + MAKE_NOP(opline); + opline->opcode = ZEND_HANDLE_EXCEPTION; + opline->lineno = opline[-1].lineno; + opline++; +#endif + + op_array->last = opline-new_opcodes; + + /* adjust exception jump targets */ + if (op_array->last_try_catch) { + int i; + for (i = 0; i< op_array->last_try_catch; i++) { + op_array->try_catch_array[i].try_op = cfg->try[i]->start_opline - new_opcodes; + op_array->try_catch_array[i].catch_op = cfg->catch[i]->start_opline - new_opcodes; + } + efree(cfg->try); + efree(cfg->catch); + } + + /* adjust loop jump targets */ + if (op_array->last_brk_cont) { + int i; + for (i = 0; i< op_array->last_brk_cont; i++) { + op_array->brk_cont_array[i].start = cfg->loop_start[i]->start_opline - new_opcodes; + op_array->brk_cont_array[i].cont = cfg->loop_cont[i]->start_opline - new_opcodes; + op_array->brk_cont_array[i].brk = cfg->loop_brk[i]->start_opline - new_opcodes; + } + efree(cfg->loop_start); + efree(cfg->loop_cont); + efree(cfg->loop_brk); + } + + /* adjust jump targets */ + for (cur_block = blocks; cur_block; cur_block = cur_block->next) { + if (!cur_block->access) { + continue; + } + if (cur_block->op1_to) { + ZEND_OP1(&cur_block->start_opline[cur_block->len - 1]).opline_num = cur_block->op1_to->start_opline - new_opcodes; + } + if (cur_block->op2_to) { + ZEND_OP2(&cur_block->start_opline[cur_block->len - 1]).opline_num = cur_block->op2_to->start_opline - new_opcodes; + } + if (cur_block->ext_to) { + cur_block->start_opline[cur_block->len - 1].extended_value = cur_block->ext_to->start_opline - new_opcodes; + } + print_block(cur_block, new_opcodes, "Out "); + } + efree(op_array->opcodes); + op_array->opcodes = erealloc(new_opcodes, op_array->last * sizeof(zend_op)); + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + /* adjust early binding list */ + if (op_array->early_binding != (zend_uint)-1) { + zend_uint *opline_num = &op_array->early_binding; + zend_op *end; + + opline = op_array->opcodes; + end = opline + op_array->last; + while (opline < end) { + if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) { + *opline_num = opline - op_array->opcodes; + opline_num = &ZEND_RESULT(opline).opline_num; + } + ++opline; + } + *opline_num = -1; + } +#endif +} + +static void zend_jmp_optimization(zend_code_block *block, zend_op_array *op_array, zend_code_block *blocks) +{ + /* last_op is the last opcode of the current block */ + zend_op *last_op = (block->start_opline + block->len - 1); + + if (!block->len) { + return; + } + switch (last_op->opcode) { + case ZEND_JMP: + { + zend_op *target = block->op1_to->start_opline; + zend_code_block *next = block->next; + + while (next && !next->access) { + /* find used one */ + next = next->next; + } + + /* JMP(next) -> NOP */ + if (block->op1_to == next) { + block->follow_to = block->op1_to; + block->op1_to = NULL; + MAKE_NOP(last_op); + block->len--; + if (block->len == 0) { + /* this block is nothing but NOP now */ + delete_code_block(block); + } + break; + } + + if (((target->opcode == ZEND_JMP && + block->op1_to != block->op1_to->op1_to) || + target->opcode == ZEND_JMPZNZ) && + !block->op1_to->protected) { + /* JMP L, L: JMP L1 -> JMP L1 */ + /* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */ + *last_op = *target; +#if ZEND_EXTENSION_API_NO < PHP_5_4_X_API_NO + if (ZEND_OP1_TYPE(last_op) == IS_CONST) { + zval_copy_ctor(&ZEND_OP1_LITERAL(last_op)); + } +#endif + del_source(block, block->op1_to); + if (block->op1_to->op2_to) { + block->op2_to = block->op1_to->op2_to; + ADD_SOURCE(block, block->op2_to); + } + if (block->op1_to->ext_to) { + block->ext_to = block->op1_to->ext_to; + ADD_SOURCE(block, block->ext_to); + } + if (block->op1_to->op1_to) { + block->op1_to = block->op1_to->op1_to; + ADD_SOURCE(block, block->op1_to); + } else { + block->op1_to = NULL; + } + } else if (target->opcode == ZEND_RETURN || +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + target->opcode == ZEND_RETURN_BY_REF || +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + target->opcode == ZEND_FAST_RET || +#endif + target->opcode == ZEND_EXIT) { + /* JMP L, L: RETURN to immediate RETURN */ + *last_op = *target; +#if ZEND_EXTENSION_API_NO < PHP_5_4_X_API_NO + if (ZEND_OP1_TYPE(last_op) == IS_CONST) { + zval_copy_ctor(&ZEND_OP1_LITERAL(last_op)); + } +#endif + del_source(block, block->op1_to); + block->op1_to = NULL; +#if 0 + /* Temporarily disabled - see bug #0025274 */ + } else if (0&& block->op1_to != block && + block->op1_to != blocks && + op_array->last_try_catch == 0 && + target->opcode != ZEND_FREE && + target->opcode != ZEND_SWITCH_FREE) { + /* Block Reordering (saves one JMP on each "for" loop iteration) + * It is disabled for some cases (ZEND_FREE/ZEND_SWITCH_FREE) + * which may break register allocation. + */ + zend_bool can_reorder = 0; + zend_block_source *cs = block->op1_to->sources; + + /* the "target" block doesn't had any followed block */ + while(cs) { + if (cs->from->follow_to == block->op1_to) { + can_reorder = 0; + break; + } + cs = cs->next; + } + if (can_reorder) { + next = block->op1_to; + /* the "target" block is not followed by current "block" */ + while (next->follow_to != NULL) { + if (next->follow_to == block) { + can_reorder = 0; + break; + } + next = next->follow_to; + } + if (can_reorder) { + zend_code_block *prev = blocks; + + while (prev->next != block->op1_to) { + prev = prev->next; + } + prev->next = next->next; + next->next = block->next; + block->next = block->op1_to; + + block->follow_to = block->op1_to; + block->op1_to = NULL; + MAKE_NOP(last_op); + block->len--; + if(block->len == 0) { + /* this block is nothing but NOP now */ + delete_code_block(block); + } + break; + } + } +#endif + } + } + break; + + case ZEND_JMPZ: + case ZEND_JMPNZ: + /* constant conditional JMPs */ + if (ZEND_OP1_TYPE(last_op) == IS_CONST) { + int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op)); + if (last_op->opcode == ZEND_JMPZ) { + should_jmp = !should_jmp; + } + literal_dtor(&ZEND_OP1_LITERAL(last_op)); + ZEND_OP1_TYPE(last_op) = IS_UNUSED; + if (should_jmp) { + /* JMPNZ(true) -> JMP */ + last_op->opcode = ZEND_JMP; + COPY_NODE(last_op->op1, last_op->op2); + block->op1_to = block->op2_to; + del_source(block, block->follow_to); + block->op2_to = NULL; + block->follow_to = NULL; + } else { + /* JMPNZ(false) -> NOP */ + MAKE_NOP(last_op); + del_source(block, block->op2_to); + block->op2_to = NULL; + } + break; + } + + if (block->op2_to) { + zend_uchar same_type = ZEND_OP1_TYPE(last_op); + zend_uint same_var = VAR_NUM_EX(last_op->op1); + zend_op *target; + zend_op *target_end; + zend_code_block *target_block = block->op2_to;; + +next_target: + target = target_block->start_opline; + target_end = target_block->start_opline + target_block->len; + while (target < target_end && target->opcode == ZEND_NOP) { + target++; + } + + /* next block is only NOP's */ + if (target == target_end) { + target_block = target_block->follow_to; + goto next_target; + } else if (target->opcode == INV_COND(last_op->opcode) && + /* JMPZ(X, L), L: JMPNZ(X, L2) -> JMPZ(X, L+1) */ + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + target_block->follow_to && + !target_block->protected + ) { + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + } else if (target->opcode == INV_COND_EX(last_op->opcode) && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + target_block->follow_to && + !target_block->protected) { + /* JMPZ(X, L), L: X = JMPNZ_EX(X, L2) -> JMPZ(X, L+1) */ + last_op->opcode += 3; + last_op->result = target->result; + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + } else if (target_block->op2_to && + target->opcode == last_op->opcode && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + !target_block->protected) { + /* JMPZ(X, L), L: JMPZ(X, L2) -> JMPZ(X, L2) */ + del_source(block, block->op2_to); + block->op2_to = target_block->op2_to; + ADD_SOURCE(block, block->op2_to); + } else if (target_block->op1_to && + target->opcode == ZEND_JMP && + !target_block->protected) { + /* JMPZ(X, L), L: JMP(L2) -> JMPZ(X, L2) */ + del_source(block, block->op2_to); + block->op2_to = target_block->op1_to; + ADD_SOURCE(block, block->op2_to); + } else if (target_block->op2_to && + target_block->ext_to && + target->opcode == ZEND_JMPZNZ && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + !target_block->protected) { + /* JMPZ(X, L), L: JMPZNZ(X, L2, L3) -> JMPZ(X, L2) */ + del_source(block, block->op2_to); + if (last_op->opcode == ZEND_JMPZ) { + block->op2_to = target_block->op2_to; + } else { + block->op2_to = target_block->ext_to; + } + ADD_SOURCE(block, block->op2_to); + } + } + + if (block->follow_to && + (last_op->opcode == ZEND_JMPZ || last_op->opcode == ZEND_JMPNZ)) { + zend_op *target; + zend_op *target_end; + + while (1) { + target = block->follow_to->start_opline; + target_end = block->follow_to->start_opline + block->follow_to->len; + while (target < target_end && target->opcode == ZEND_NOP) { + target++; + } + + /* next block is only NOP's */ + if (target == target_end) { + del_source(block, block->follow_to); + block->follow_to = block->follow_to->follow_to; + ADD_SOURCE(block, block->follow_to); + } else { + break; + } + } + /* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */ + if (target->opcode == ZEND_JMP && + block->follow_to->op1_to && + !block->follow_to->protected) { + del_source(block, block->follow_to); + if (last_op->opcode == ZEND_JMPZ) { + block->ext_to = block->follow_to->op1_to; + ADD_SOURCE(block, block->ext_to); + } else { + block->ext_to = block->op2_to; + block->op2_to = block->follow_to->op1_to; + ADD_SOURCE(block, block->op2_to); + } + block->follow_to = NULL; + last_op->opcode = ZEND_JMPZNZ; + } + } + break; + + case ZEND_JMPNZ_EX: + case ZEND_JMPZ_EX: + /* constant conditional JMPs */ + if (ZEND_OP1_TYPE(last_op) == IS_CONST) { + int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(last_op)); + if (last_op->opcode == ZEND_JMPZ_EX) { + should_jmp = !should_jmp; + } + if (!should_jmp) { + /* T = JMPZ_EX(true,L) -> T = QM_ASSIGN(true) + * T = JMPNZ_EX(false,L) -> T = QM_ASSIGN(false) + */ + last_op->opcode = ZEND_QM_ASSIGN; + SET_UNUSED(last_op->op2); + del_source(block, block->op2_to); + block->op2_to = NULL; + } + break; + } + + if (block->op2_to) { + zend_op *target, *target_end; + char *same_t=NULL; + zend_code_block *target_block; + int var_num = 0; + if (op_array->T >= (zend_uint)op_array->last_var) { + var_num = op_array->T; + } else { + var_num = op_array->last_var; + } + if (var_num <= 0) { + return; + } + same_t = ecalloc(var_num, sizeof(char)); + if (same_t == NULL) { + return; + } + same_t[VAR_NUM_EX(last_op->op1)] |= ZEND_OP1_TYPE(last_op); + same_t[VAR_NUM_EX(last_op->result)] |= ZEND_RESULT_TYPE(last_op); + target_block = block->op2_to; +next_target_ex: + target = target_block->start_opline; + target_end = target_block->start_opline + target_block->len; + while (target < target_end && target->opcode == ZEND_NOP) { + target++; + } + /* next block is only NOP's */ + if (target == target_end) { + target_block = target_block->follow_to; + goto next_target_ex; + } else if (target_block->op2_to && + target->opcode == last_op->opcode-3 && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + !target_block->protected) { + /* T = JMPZ_EX(X, L1), L1: JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */ + del_source(block, block->op2_to); + block->op2_to = target_block->op2_to; + ADD_SOURCE(block, block->op2_to); + } else if (target_block->op2_to && + target->opcode == INV_EX_COND(last_op->opcode) && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + !target_block->protected) { + /* T = JMPZ_EX(X, L1), L1: JMPNZ({X|T1}, L2) -> T = JMPZ_EX(X, L1+1) */ + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + } else if (target_block->op2_to && + target->opcode == INV_EX_COND_EX(last_op->opcode) && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 && + !target_block->protected) { + /* T = JMPZ_EX(X, L1), L1: T = JMPNZ_EX(T, L2) -> T = JMPZ_EX(X, L1+1) */ + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + } else if (target_block->op2_to && + target->opcode == last_op->opcode && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + (same_t[VAR_NUM_EX(target->result)] & ZEND_RESULT_TYPE(target)) != 0 && + !target_block->protected) { + /* T = JMPZ_EX(X, L1), L1: T = JMPZ({X|T}, L2) -> T = JMPZ_EX(X, L2) */ + del_source(block, block->op2_to); + block->op2_to = target_block->op2_to; + ADD_SOURCE(block, block->op2_to); + } else if (target_block->op1_to && + target->opcode == ZEND_JMP && + !target_block->protected) { + /* T = JMPZ_EX(X, L), L: JMP(L2) -> T = JMPZ(X, L2) */ + del_source(block, block->op2_to); + block->op2_to = target_block->op1_to; + ADD_SOURCE(block, block->op2_to); + } else if (target_block->op2_to && + target_block->ext_to && + target->opcode == ZEND_JMPZNZ && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + (same_t[VAR_NUM_EX(target->op1)] & ZEND_OP1_TYPE(target)) != 0 && + !target_block->protected) { + /* T = JMPZ_EX(X, L), L: JMPZNZ({X|T}, L2, L3) -> T = JMPZ_EX(X, L2) */ + del_source(block, block->op2_to); + if (last_op->opcode == ZEND_JMPZ_EX) { + block->op2_to = target_block->op2_to; + } else { + block->op2_to = target_block->ext_to; + } + ADD_SOURCE(block, block->op2_to); + } + if (same_t != NULL) { + efree(same_t); + } + } + break; + + case ZEND_JMPZNZ: { + zend_code_block *next = block->next; + + while (next && !next->access) { + /* find first accessed one */ + next = next->next; + } + + if (ZEND_OP1_TYPE(last_op) == IS_CONST) { + if (!zend_is_true(&ZEND_OP1_LITERAL(last_op))) { + /* JMPZNZ(false,L1,L2) -> JMP(L1) */ + zend_code_block *todel; + + literal_dtor(&ZEND_OP1_LITERAL(last_op)); + last_op->opcode = ZEND_JMP; + SET_UNUSED(last_op->op1); + SET_UNUSED(last_op->op2); + block->op1_to = block->op2_to; + todel = block->ext_to; + block->op2_to = NULL; + block->ext_to = NULL; + del_source(block, todel); + } else { + /* JMPZNZ(true,L1,L2) -> JMP(L2) */ + zend_code_block *todel; + + literal_dtor(&ZEND_OP1_LITERAL(last_op)); + last_op->opcode = ZEND_JMP; + SET_UNUSED(last_op->op1); + SET_UNUSED(last_op->op2); + block->op1_to = block->ext_to; + todel = block->op2_to; + block->op2_to = NULL; + block->ext_to = NULL; + del_source(block, todel); + } + } else if (block->op2_to == block->ext_to) { + /* both goto the same one - it's JMP */ + /* JMPZNZ(?,L,L) -> JMP(L) */ + last_op->opcode = ZEND_JMP; + SET_UNUSED(last_op->op1); + SET_UNUSED(last_op->op2); + block->op1_to = block->op2_to; + block->op2_to = NULL; + block->ext_to = NULL; + } else if (block->op2_to == next) { + /* jumping to next on Z - can follow to it and jump only on NZ */ + /* JMPZNZ(X,L1,L2) L1: -> JMPNZ(X,L2) */ + last_op->opcode = ZEND_JMPNZ; + block->op2_to = block->ext_to; + block->follow_to = next; + block->ext_to = NULL; + /* no need to add source - it's block->op2_to */ + } else if (block->ext_to == next) { + /* jumping to next on NZ - can follow to it and jump only on Z */ + /* JMPZNZ(X,L1,L2) L2: -> JMPZ(X,L1) */ + last_op->opcode = ZEND_JMPZ; + block->follow_to = next; + block->ext_to = NULL; + /* no need to add source - it's block->ext_to */ + } + + if (last_op->opcode == ZEND_JMPZNZ && block->op2_to) { + zend_uchar same_type = ZEND_OP1_TYPE(last_op); + zend_uchar same_var = VAR_NUM_EX(last_op->op1); + zend_op *target; + zend_op *target_end; + zend_code_block *target_block = block->op2_to; + +next_target_znz: + target = target_block->start_opline; + target_end = target_block->start_opline + target_block->len; + while (target < target_end && target->opcode == ZEND_NOP) { + target++; + } + /* next block is only NOP's */ + if (target == target_end) { + target_block = target_block->follow_to; + goto next_target_znz; + } else if (target_block->op2_to && + (target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + !target_block->protected) { + /* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */ + del_source(block, block->op2_to); + block->op2_to = target_block->op2_to; + ADD_SOURCE(block, block->op2_to); + } else if (target->opcode == ZEND_JMPNZ && + (ZEND_OP1_TYPE(target) & (IS_TMP_VAR|IS_CV)) && + same_type == ZEND_OP1_TYPE(target) && + same_var == VAR_NUM_EX(target->op1) && + target_block->follow_to && + !target_block->protected) { + /* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */ + del_source(block, block->op2_to); + block->op2_to = target_block->follow_to; + ADD_SOURCE(block, block->op2_to); + } else if (target_block->op1_to && + target->opcode == ZEND_JMP && + !target_block->protected) { + /* JMPZNZ(X, L1, L2), L1: JMP(L3) -> JMPZNZ(X, L3, L2) */ + del_source(block, block->op2_to); + block->op2_to = target_block->op1_to; + ADD_SOURCE(block, block->op2_to); + } + } + break; + } + } +} + +/* Global data dependencies */ + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + +# define T_USAGE(op) do { \ + if ((op ## _type & (IS_VAR | IS_TMP_VAR)) && \ + !defined_here[VAR_NUM(op.var)] && !used_ext[VAR_NUM(op.var)]) { \ + used_ext[VAR_NUM(op.var)] = 1; \ + } \ + } while (0) + +# define NEVER_USED(op) ((op ## _type & (IS_VAR | IS_TMP_VAR)) && !usage[VAR_NUM(op.var)]) /* !used_ext[op.var] && */ +# define RES_NEVER_USED(opline) (opline->result_type == IS_UNUSED || NEVER_USED(opline->result)) + +#else + +# define T_USAGE(op) do { \ + if ((op.op_type == IS_VAR || op.op_type == IS_TMP_VAR) && \ + !defined_here[VAR_NUM(op.u.var)] && !used_ext[VAR_NUM(op.u.var)]) { \ + used_ext[VAR_NUM(op.u.var)] = 1; \ + } \ + } while (0) + +# define NEVER_USED(op) ((op.op_type == IS_VAR || op.op_type == IS_TMP_VAR) && !usage[VAR_NUM(op.u.var)]) /* !used_ext[op.u.var] && */ +# define RES_NEVER_USED(opline) (ZEND_RESULT_TYPE(opline) == IS_UNUSED || NEVER_USED(opline->result)) + +#endif + +/* Find a set of variables which are used outside of the block where they are + * defined. We won't apply some optimization patterns for sush variables. */ +static void zend_t_usage(zend_code_block *block, zend_op_array *op_array, char *used_ext) +{ + zend_code_block *next_block = block->next; + char *usage; + char *defined_here; + + if (op_array->T == 0) { + /* shortcut - if no Ts, nothing to do */ + return; + } + + usage = ecalloc(op_array->T, 1); + defined_here = emalloc(op_array->T); + + while (next_block) { + zend_op *opline = next_block->start_opline; + zend_op *end = opline + next_block->len; + + if (!next_block->access) { + next_block = next_block->next; + continue; + } + memset(defined_here, 0, op_array->T); + + while (opline<end) { + T_USAGE(opline->op1); + T_USAGE(opline->op2); + + if (RESULT_USED(opline)) { + if (!defined_here[VAR_NUM(ZEND_RESULT(opline).var)] && !used_ext[VAR_NUM(ZEND_RESULT(opline).var)] && + (opline->opcode == ZEND_RECV || opline->opcode == ZEND_RECV_INIT || + (opline->opcode == ZEND_OP_DATA && ZEND_RESULT_TYPE(opline) == IS_TMP_VAR) || + opline->opcode == ZEND_ADD_ARRAY_ELEMENT)) { + /* these opcodes use the result as argument */ + used_ext[VAR_NUM(ZEND_RESULT(opline).var)] = 1; + } + defined_here[VAR_NUM(ZEND_RESULT(opline).var)] = 1; + } + opline++; + } + next_block = next_block->next; + } + +#if DEBUG_BLOCKPASS + { + int i; + for (i = 0; i< op_array->T; i++) { + fprintf(stderr, "T%d: %c\n", i, used_ext[i] + '0'); + } + } +#endif + + while (block) { + zend_op *opline = block->start_opline + block->len - 1; + + if (!block->access) { + block = block->next; + continue; + } + + memcpy(usage, used_ext, op_array->T); + + while (opline >= block->start_opline) { + /* usage checks */ + if (RES_NEVER_USED(opline)) { + switch (opline->opcode) { + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_BW_OR: + case ZEND_ASSIGN_BW_AND: + case ZEND_ASSIGN_BW_XOR: + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + case ZEND_POST_INC: + case ZEND_POST_DEC: + case ZEND_ASSIGN: + case ZEND_ASSIGN_REF: + case ZEND_DO_FCALL: + case ZEND_DO_FCALL_BY_NAME: + if (ZEND_RESULT_TYPE(opline) == IS_VAR) { +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ZEND_RESULT_TYPE(opline) |= EXT_TYPE_UNUSED; +#else + ZEND_RESULT(opline).EA.type |= EXT_TYPE_UNUSED; +#endif + } + break; + case ZEND_QM_ASSIGN: + case ZEND_BOOL: + case ZEND_BOOL_NOT: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + literal_dtor(&ZEND_OP1_LITERAL(opline)); + } + MAKE_NOP(opline); + break; + case ZEND_PRINT: + opline->opcode = ZEND_ECHO; + ZEND_RESULT_TYPE(opline) = IS_UNUSED; + break; + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + opline->opcode -= 3; + SET_UNUSED(opline->result); + break; + } + } + + if (opline->opcode == ZEND_RECV || + opline->opcode == ZEND_RECV_INIT || + opline->opcode == ZEND_ADD_ARRAY_ELEMENT) { + if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) { + usage[VAR_NUM(ZEND_RESULT(opline).var)] = 1; + } + } else { + if (RESULT_USED(opline)) { + usage[VAR_NUM(ZEND_RESULT(opline).var)] = 0; + } + } + + if (ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_TMP_VAR) { + usage[VAR_NUM(ZEND_OP1(opline).var)] = 1; + } + if (ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_TMP_VAR) { + usage[VAR_NUM(ZEND_OP2(opline).var)] = 1; + } + + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if ((ZEND_RESULT_TYPE(opline) & IS_VAR) && + (ZEND_RESULT_TYPE(opline) & EXT_TYPE_UNUSED) && + usage[VAR_NUM(ZEND_RESULT(opline).var)]) { + ZEND_RESULT_TYPE(opline) &= ~EXT_TYPE_UNUSED; + } +#else + if (ZEND_RESULT_TYPE(opline) == IS_VAR && + usage[VAR_NUM(ZEND_RESULT(opline).var)] && + (ZEND_RESULT(opline).EA.type & EXT_TYPE_UNUSED) != 0) { + ZEND_RESULT(opline).EA.type &= ~EXT_TYPE_UNUSED; + } +#endif + + opline--; + } + block = block->next; + } /* end blocks */ + + efree(defined_here); + efree(usage); +} + +#define PASSES 3 + +static void zend_block_optimization(zend_op_array *op_array TSRMLS_DC) +{ + zend_cfg cfg; + zend_code_block *cur_block; + int pass; + char *usage; + +#if DEBUG_BLOCKPASS + fprintf(stderr, "File %s func %s\n", op_array->filename, op_array->function_name? op_array->function_name : "main"); + fflush(stderr); +#endif + +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + return; + } +#endif + + /* Build CFG */ + if (!find_code_blocks(op_array, &cfg)) { + return; + } + + zend_rebuild_access_path(&cfg, op_array, 0); + /* full rebuild here to produce correct sources! */ + usage = emalloc(op_array->T); + for (pass = 0; pass < PASSES; pass++) { + /* Compute data dependencies */ + memset(usage, 0, op_array->T); + zend_t_usage(cfg.blocks, op_array, usage); + + /* optimize each basic block separately */ + for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) { + if (!cur_block->access) { + continue; + } + zend_optimize_block(cur_block, op_array, usage TSRMLS_CC); + } + + /* Jump optimization for each block */ + for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) { + if (!cur_block->access) { + continue; + } + zend_jmp_optimization(cur_block, op_array, cfg.blocks); + } + + /* Eliminate unreachable basic blocks */ + zend_rebuild_access_path(&cfg, op_array, 1); + } + + memset(usage, 0, op_array->T); + zend_t_usage(cfg.blocks, op_array, usage); + assemble_code_blocks(&cfg, op_array); + efree(usage); + + /* Destroy CFG */ + for (cur_block = cfg.blocks; cur_block; cur_block = cur_block->next) { + zend_block_source *cs = cur_block->sources; + while (cs) { + zend_block_source *n = cs->next; + efree(cs); + cs = n; + } + } + efree(cfg.blocks); +} diff --git a/ext/opcache/Optimizer/nop_removal.c b/ext/opcache/Optimizer/nop_removal.c new file mode 100644 index 000000000..b2fb667ed --- /dev/null +++ b/ext/opcache/Optimizer/nop_removal.c @@ -0,0 +1,126 @@ +/* pass 10: + * - remove NOPs + */ + +static void nop_removal(zend_op_array *op_array) +{ + zend_op *end, *opline; + zend_uint new_count, i, shift; + int j; + zend_uint *shiftlist; + ALLOCA_FLAG(use_heap); + + shiftlist = (zend_uint *)DO_ALLOCA(sizeof(zend_uint) * op_array->last); + i = new_count = shift = 0; + end = op_array->opcodes + op_array->last; + for (opline = op_array->opcodes; opline < end; opline++) { + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + /* GOTO target is unresolved yet. We can't optimize. */ + if (opline->opcode == ZEND_GOTO && + Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) { + /* TODO: in general we can avoid this restriction */ + FREE_ALLOCA(shiftlist); + return; + } +#endif + + /* Kill JMP-over-NOP-s */ + if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) { + /* check if there are only NOPs under the branch */ + zend_op *target = op_array->opcodes + ZEND_OP1(opline).opline_num - 1; + + while (target->opcode == ZEND_NOP) { + target--; + } + if (target == opline) { + /* only NOPs */ + opline->opcode = ZEND_NOP; + } + } + + shiftlist[i++] = shift; + if (opline->opcode == ZEND_NOP) { + shift++; + } else { + if (shift) { + op_array->opcodes[new_count] = *opline; + } + new_count++; + } + } + + if (shift) { + op_array->last = new_count; + end = op_array->opcodes + op_array->last; + + /* update JMPs */ + for (opline = op_array->opcodes; opline<end; opline++) { + switch (opline->opcode) { + case ZEND_JMP: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_GOTO: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_CALL: +#endif + ZEND_OP1(opline).opline_num -= shiftlist[ZEND_OP1(opline).opline_num]; + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_FE_FETCH: + case ZEND_FE_RESET: + case ZEND_NEW: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_JMP_SET: +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num]; + break; + case ZEND_JMPZNZ: + ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num]; + opline->extended_value -= shiftlist[opline->extended_value]; + break; + case ZEND_CATCH: + opline->extended_value -= shiftlist[opline->extended_value]; + break; + } + } + + /* update brk/cont array */ + for (j = 0; j < op_array->last_brk_cont; j++) { + op_array->brk_cont_array[j].brk -= shiftlist[op_array->brk_cont_array[j].brk]; + op_array->brk_cont_array[j].cont -= shiftlist[op_array->brk_cont_array[j].cont]; + op_array->brk_cont_array[j].start -= shiftlist[op_array->brk_cont_array[j].start]; + } + + /* update try/catch array */ + for (j = 0; j < op_array->last_try_catch; j++) { + op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op]; + op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op]; +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->try_catch_array[j].finally_op) { + op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op]; + op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end]; + } +#endif + } + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + /* update early binding list */ + if (op_array->early_binding != (zend_uint)-1) { + zend_uint *opline_num = &op_array->early_binding; + + do { + *opline_num -= shiftlist[*opline_num]; + opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num; + } while (*opline_num != (zend_uint)-1); + } +#endif + } + FREE_ALLOCA(shiftlist); +} diff --git a/ext/opcache/Optimizer/optimize_temp_vars_5.c b/ext/opcache/Optimizer/optimize_temp_vars_5.c new file mode 100644 index 000000000..dc630733f --- /dev/null +++ b/ext/opcache/Optimizer/optimize_temp_vars_5.c @@ -0,0 +1,222 @@ +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + +/* ops that use CLs: +op1: +ZEND_FETCH_CONSTANT: +ZEND_INIT_CTOR_CALL: +ZEND_INIT_STATIC_METHOD_CALL: +ZEND_INIT_METHOD_CALL: +ZEND_IMPORT_CLASS: +ZEND_IMPORT_FUNCTION: +ZEND_IMPORT_CONST: +ZEND_ADD_INTERFACE: +ZEND_VERIFY_ABSTRACT_CLASS: +ZEND_NEW: +ZEND_CATCH: +ZEND_INIT_FCALL_BY_NAME: + +op2: +ZEND_UNSET_VAR: +ZEND_ISSET_ISEMPTY_VAR: +ZEND_FETCH_UNSET: +ZEND_FETCH_IS: +ZEND_FETCH_R: +ZEND_FETCH_W: +ZEND_FETCH_RW: +ZEND_FETCH_FUNC_ARG: +ZEND_ADD_INTERFACE: +ZEND_INSTANCEOF: + +extended_value: +ZEND_DECLARE_INHERITED_CLASS: + +ignore result +INIT_METHOD_CALL: +*/ + +#define OP1_CONST_IS_CLASS 1 +#define OP2_CONST_IS_CLASS 2 +#define EXT_CONST_IS_CLASS 4 +#define RESULT_IS_UNUSED 8 + +static const char op_const_means_class[256] = { + /* 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 32 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + /* 64 */ + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, + /* 96 */ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 9, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 128 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 160 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 192 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 224 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + +#define GET_AVAILABLE_T() \ + for (i = 0; i < T; i++) { \ + if (!taken_T[i]) { \ + break; \ + } \ + } \ + taken_T[i] = 1; \ + if (i > max) { \ + max = i; \ + } + +static void optimize_temporary_variables(zend_op_array *op_array) +{ + int T = op_array->T; + char *taken_T; /* T index in use */ + zend_op **start_of_T; /* opline where T is first used */ + char *valid_T; /* Is the map_T valid */ + int *map_T; /* Map's the T to its new index */ + zend_op *opline, *end; + int currT; + int i; + int max = -1; + int var_to_free = -1; + + taken_T = (char *) emalloc(T); + start_of_T = (zend_op **) emalloc(T * sizeof(zend_op *)); + valid_T = (char *) emalloc(T); + map_T = (int *) emalloc(T * sizeof(int)); + + end = op_array->opcodes; + opline = &op_array->opcodes[op_array->last - 1]; + + /* Find T definition points */ + while (opline >= end) { +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) { + if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) { + start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline; + } + } +#else + if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { + start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline; + } +#endif + opline--; + } + + memset(valid_T, 0, T); + memset(taken_T, 0, T); + + end = op_array->opcodes; + opline = &op_array->opcodes[op_array->last - 1]; + + while (opline >= end) { + if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + || ((op_const_means_class[opline->opcode] & OP1_CONST_IS_CLASS) && ZEND_OP1_TYPE(opline) == IS_CONST) +#endif + ) { + currT = VAR_NUM(ZEND_OP1(opline).var); + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + } + ZEND_OP1(opline).var = NUM_VAR(map_T[currT]); + } + + /* Skip OP_DATA */ + if (opline->opcode == ZEND_OP_DATA && + (opline-1)->opcode == ZEND_ASSIGN_DIM) { + opline--; + continue; + } + + if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + || ((op_const_means_class[opline->opcode] & OP2_CONST_IS_CLASS) && ZEND_OP2_TYPE(opline) == IS_CONST) +#endif + ) { + currT = VAR_NUM(ZEND_OP2(opline).var); + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + } + ZEND_OP2(opline).var = NUM_VAR(map_T[currT]); + } + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if ((op_const_means_class[opline->opcode] & EXT_CONST_IS_CLASS)) { +#else + if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS || + opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) { +#endif + currT = VAR_NUM(opline->extended_value); + if (!valid_T[currT]) { + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + } + opline->extended_value = NUM_VAR(map_T[currT]); + } + + /* Allocate OP_DATA->op2 after "operands", but before "result" */ + if (opline->opcode == ZEND_ASSIGN_DIM && + (opline + 1)->opcode == ZEND_OP_DATA && + ZEND_OP2_TYPE(opline + 1) & (IS_VAR | IS_TMP_VAR)) { + currT = VAR_NUM(ZEND_OP2(opline + 1).var); + GET_AVAILABLE_T(); + map_T[currT] = i; + valid_T[currT] = 1; + taken_T[i] = 0; + ZEND_OP2(opline + 1).var = NUM_VAR(i); + var_to_free = i; + } + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) { + if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) { +#else + if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) { +#endif + currT = VAR_NUM(ZEND_RESULT(opline).var); + if (valid_T[currT]) { + if (start_of_T[currT] == opline) { + taken_T[map_T[currT]] = 0; + } + ZEND_RESULT(opline).var = NUM_VAR(map_T[currT]); + } else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */ + GET_AVAILABLE_T(); + + if (RESULT_UNUSED(opline)) { + taken_T[i] = 0; + } else { + /* Code which gets here is using a wrongly built opcode such as RECV() */ + map_T[currT] = i; + valid_T[currT] = 1; + } + ZEND_RESULT(opline).var = NUM_VAR(i); + } +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + } +#endif + } + + if (var_to_free >= 0) { + taken_T[var_to_free] = 0; + var_to_free = -1; + } + + opline--; + } + + efree(taken_T); + efree(start_of_T); + efree(valid_T); + efree(map_T); + op_array->T = max + 1; +} diff --git a/ext/opcache/Optimizer/pass10.c b/ext/opcache/Optimizer/pass10.c new file mode 100644 index 000000000..3bfcec643 --- /dev/null +++ b/ext/opcache/Optimizer/pass10.c @@ -0,0 +1,3 @@ +if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & OPTIMIZATION_LEVEL) == ZEND_OPTIMIZER_PASS_10) { + nop_removal(op_array); +} diff --git a/ext/opcache/Optimizer/pass1_5.c b/ext/opcache/Optimizer/pass1_5.c new file mode 100644 index 000000000..8938e148c --- /dev/null +++ b/ext/opcache/Optimizer/pass1_5.c @@ -0,0 +1,391 @@ +/* pass 1 + * - substitute persistent constants (true, false, null, etc) + * - perform compile-time evaluation of constant binary and unary operations + * - optimize series of ADD_STRING and/or ADD_CHAR + * - convert CAST(IS_BOOL,x) into BOOL(x) + * - convert INTI_FCALL_BY_NAME, DO_FCALL_BY_NAME into DO_FCALL + */ + +if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) { + int i = 0; + zend_op *opline = op_array->opcodes; + zend_op *end = opline + op_array->last; + + while (opline < end) { + switch (opline->opcode) { + case ZEND_ADD: + case ZEND_SUB: + case ZEND_MUL: + case ZEND_DIV: + case ZEND_MOD: + case ZEND_SL: + case ZEND_SR: + case ZEND_CONCAT: + case ZEND_IS_EQUAL: + case ZEND_IS_NOT_EQUAL: + case ZEND_IS_SMALLER: + case ZEND_IS_SMALLER_OR_EQUAL: + case ZEND_IS_IDENTICAL: + case ZEND_IS_NOT_IDENTICAL: + case ZEND_BW_OR: + case ZEND_BW_AND: + case ZEND_BW_XOR: + case ZEND_BOOL_XOR: + if (ZEND_OP1_TYPE(opline) == IS_CONST && + ZEND_OP2_TYPE(opline) == IS_CONST) { + /* binary operation with constant operands */ + int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) = get_binary_op(opline->opcode); + zend_uint tv = ZEND_RESULT(opline).var; /* temporary variable */ + zval result; + zend_op *tmp_opline; + int er; + + if (opline->opcode == ZEND_DIV && + Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG && + Z_LVAL(ZEND_OP2_LITERAL(opline)) == 0) { + /* div by 0 */ + break; + } + er = EG(error_reporting); + EG(error_reporting) = 0; + /* evaluate constant expression */ + if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline) TSRMLS_CC) != SUCCESS) { + EG(error_reporting) = er; + break; + } + EG(error_reporting) = er; + PZ_SET_REFCOUNT_P(&result, 1); + PZ_UNSET_ISREF_P(&result); + + literal_dtor(&ZEND_OP1_LITERAL(opline)); + literal_dtor(&ZEND_OP2_LITERAL(opline)); + MAKE_NOP(opline); + + /* substitute the following TMP_VAR usage with constant */ + for (tmp_opline = opline + 1; tmp_opline < end; tmp_opline++) { + if (ZEND_OP1_TYPE(tmp_opline) == IS_TMP_VAR && + ZEND_OP1(tmp_opline).var == tv) { + if (tmp_opline->opcode == ZEND_FREE) { + MAKE_NOP(tmp_opline); + zval_dtor(&result); + } else { + ZEND_OP1_TYPE(tmp_opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + tmp_opline->op1.constant = zend_optimizer_add_literal(op_array, &result TSRMLS_CC); + if (Z_TYPE(result) == IS_STRING) { + Z_HASH_P(&ZEND_OP1_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP1_LITERAL(tmp_opline)) + 1); + if (tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || + tmp_opline->opcode == ZEND_DO_FCALL || + tmp_opline->opcode == ZEND_CATCH || + tmp_opline->opcode == ZEND_FETCH_CONSTANT) { + op_array->literals[tmp_opline->op1.constant].cache_slot = op_array->last_cache_slot++; + } + } +#else + ZEND_OP1_LITERAL(tmp_opline) = result; +#endif + } + /* TMP_VAR my be used only once */ + break; + } + if (ZEND_OP2_TYPE(tmp_opline) == IS_TMP_VAR && + ZEND_OP2(tmp_opline).var == tv) { + ZEND_OP2_TYPE(tmp_opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + tmp_opline->op2.constant = zend_optimizer_add_literal(op_array, &result TSRMLS_CC); + if (Z_TYPE(result) == IS_STRING) { + Z_HASH_P(&ZEND_OP2_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP2_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP2_LITERAL(tmp_opline)) + 1); + if (tmp_opline->opcode == ZEND_FETCH_R || + tmp_opline->opcode == ZEND_FETCH_W || + tmp_opline->opcode == ZEND_FETCH_RW || + tmp_opline->opcode == ZEND_FETCH_IS || + tmp_opline->opcode == ZEND_FETCH_UNSET || + tmp_opline->opcode == ZEND_FETCH_FUNC_ARG || + tmp_opline->opcode == ZEND_FETCH_CLASS || + tmp_opline->opcode == ZEND_INIT_FCALL_BY_NAME || + tmp_opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME || + tmp_opline->opcode == ZEND_UNSET_VAR || + tmp_opline->opcode == ZEND_ISSET_ISEMPTY_VAR || + tmp_opline->opcode == ZEND_ADD_INTERFACE || + tmp_opline->opcode == ZEND_ADD_TRAIT) { + op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot++; + } else if (tmp_opline->opcode == ZEND_INIT_METHOD_CALL || + tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL || + tmp_opline->opcode == ZEND_FETCH_CONSTANT || + tmp_opline->opcode == ZEND_ASSIGN_OBJ || + tmp_opline->opcode == ZEND_FETCH_OBJ_R || + tmp_opline->opcode == ZEND_FETCH_OBJ_W || + tmp_opline->opcode == ZEND_FETCH_OBJ_RW || + tmp_opline->opcode == ZEND_FETCH_OBJ_IS || + tmp_opline->opcode == ZEND_FETCH_OBJ_UNSET || + tmp_opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG || + tmp_opline->opcode == ZEND_UNSET_OBJ || + tmp_opline->opcode == ZEND_PRE_INC_OBJ || + tmp_opline->opcode == ZEND_PRE_DEC_OBJ || + tmp_opline->opcode == ZEND_POST_INC_OBJ || + tmp_opline->opcode == ZEND_POST_DEC_OBJ || + tmp_opline->opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ) { + op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot; + op_array->last_cache_slot += 2; + } else if (tmp_opline->opcode == ZEND_ASSIGN_ADD || + tmp_opline->opcode == ZEND_ASSIGN_SUB || + tmp_opline->opcode == ZEND_ASSIGN_MUL || + tmp_opline->opcode == ZEND_ASSIGN_DIV || + tmp_opline->opcode == ZEND_ASSIGN_MOD || + tmp_opline->opcode == ZEND_ASSIGN_SL || + tmp_opline->opcode == ZEND_ASSIGN_SR || + tmp_opline->opcode == ZEND_ASSIGN_CONCAT || + tmp_opline->opcode == ZEND_ASSIGN_BW_OR || + tmp_opline->opcode == ZEND_ASSIGN_BW_AND || + tmp_opline->opcode == ZEND_ASSIGN_BW_XOR) { + if (tmp_opline->extended_value == ZEND_ASSIGN_OBJ) { + op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot; + op_array->last_cache_slot += 2; + } + } + } +#else + ZEND_OP2_LITERAL(tmp_opline) = result; +#endif + break; + } + } + } + break; + + case ZEND_CAST: + if (ZEND_OP1_TYPE(opline) == IS_CONST && + opline->extended_value != IS_ARRAY && + opline->extended_value != IS_OBJECT && + opline->extended_value != IS_RESOURCE) { + /* cast of constant operand */ + zval res; + res = ZEND_OP1_LITERAL(opline); + zval_copy_ctor(&res); + switch (opline->extended_value) { + case IS_NULL: + convert_to_null(&res); + break; + case IS_BOOL: + convert_to_boolean(&res); + break; + case IS_LONG: + convert_to_long(&res); + break; + case IS_DOUBLE: + convert_to_double(&res); + break; + case IS_STRING: + convert_to_string(&res); + break; + } + literal_dtor(&ZEND_OP1_LITERAL(opline)); + opline->opcode = ZEND_QM_ASSIGN; + opline->extended_value = 0; + ZEND_OP1_LITERAL(opline) = res; + SET_UNUSED(opline->op2); + } else if (opline->extended_value == IS_BOOL) { + /* T = CAST(X, IS_BOOL) => T = BOOL(X) */ + opline->opcode = ZEND_BOOL; + opline->extended_value = 0; + } + break; + + case ZEND_BW_NOT: + case ZEND_BOOL_NOT: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + /* unary operation on constant operand */ + unary_op_type unary_op = get_unary_op(opline->opcode); + zval result; + zend_op *tmp_opline; + zend_uint tv = ZEND_RESULT(opline).var; /* temporary variable */ + int er; + + er = EG(error_reporting); + EG(error_reporting) = 0; + if (unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC) != SUCCESS) { + EG(error_reporting) = er; + break; + } + EG(error_reporting) = er; + PZ_SET_REFCOUNT_P(&result, 1); + PZ_UNSET_ISREF_P(&result); + + literal_dtor(&ZEND_OP1_LITERAL(opline)); + MAKE_NOP(opline); + + /* substitute the following TMP_VAR usage with constant */ + for (tmp_opline = opline + 1; tmp_opline < end; tmp_opline++) { + if (ZEND_OP1_TYPE(tmp_opline) == IS_TMP_VAR && + ZEND_OP1(tmp_opline).var == tv) { + if (tmp_opline->opcode == ZEND_FREE) { + MAKE_NOP(tmp_opline); + zval_dtor(&result); + } else { + ZEND_OP1_TYPE(tmp_opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + tmp_opline->op1.constant = zend_optimizer_add_literal(op_array, &result TSRMLS_CC); +#else + ZEND_OP1_LITERAL(tmp_opline) = result; +#endif + } + break; + } + if (ZEND_OP2_TYPE(tmp_opline) == IS_TMP_VAR && + ZEND_OP2(tmp_opline).var == tv) { + ZEND_OP2_TYPE(tmp_opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + tmp_opline->op2.constant = zend_optimizer_add_literal(op_array, &result TSRMLS_CC); +#else + ZEND_OP2_LITERAL(tmp_opline) = result; +#endif + break; + } + } + } + break; + + case ZEND_ADD_STRING: + case ZEND_ADD_CHAR: + { + zend_op *next_op = opline + 1; + int requires_conversion = (opline->opcode == ZEND_ADD_CHAR? 1 : 0); + size_t final_length = 0; + char *ptr; + zend_op *last_op; + + /* There is always a ZEND_RETURN at the end + if (next_op>=end) { + break; + } + */ + while (next_op->opcode == ZEND_ADD_STRING || next_op->opcode == ZEND_ADD_CHAR) { + if (ZEND_RESULT(opline).var != ZEND_RESULT(next_op).var) { + break; + } + if (next_op->opcode == ZEND_ADD_CHAR) { + final_length += 1; + } else { /* ZEND_ADD_STRING */ + final_length += ZEND_OP2_LITERAL(next_op).value.str.len; + } + next_op++; + } + if (final_length == 0) { + break; + } + last_op = next_op; + final_length += (requires_conversion? 1 : ZEND_OP2_LITERAL(opline).value.str.len); + ptr = (char *)emalloc(final_length + 1); + ptr[final_length] = '\0'; + if (requires_conversion) { /* ZEND_ADD_CHAR */ + char chval = (char)ZEND_OP2_LITERAL(opline).value.lval; + + ZEND_OP2_LITERAL(opline).value.str.val = ptr; + ptr[0] = chval; + ZEND_OP2_LITERAL(opline).type = IS_STRING; + opline->opcode = ZEND_ADD_STRING; + ptr++; + } else { /* ZEND_ADD_STRING */ + memcpy(ptr, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline))); + if (!IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(opline)))) { + efree(Z_STRVAL(ZEND_OP2_LITERAL(opline))); + } + Z_STRVAL(ZEND_OP2_LITERAL(opline)) = ptr; + ptr += Z_STRLEN(ZEND_OP2_LITERAL(opline)); + } + ZEND_OP2_LITERAL(opline).value.str.len = final_length; + next_op = opline + 1; + while (next_op < last_op) { + if (next_op->opcode == ZEND_ADD_STRING) { + memcpy(ptr, ZEND_OP2_LITERAL(next_op).value.str.val, ZEND_OP2_LITERAL(next_op).value.str.len); + ptr += ZEND_OP2_LITERAL(next_op).value.str.len; + literal_dtor(&ZEND_OP2_LITERAL(next_op)); + } else { /* ZEND_ADD_CHAR */ + *ptr = (char)ZEND_OP2_LITERAL(next_op).value.lval; + ptr++; + } + MAKE_NOP(next_op); + next_op++; + } + if (!((ZEND_OPTIMIZER_PASS_5|ZEND_OPTIMIZER_PASS_10) & OPTIMIZATION_LEVEL)) { + /* NOP removal is disabled => insert JMP over NOPs */ + if (last_op-opline >= 3) { /* If we have more than 2 NOPS then JMP over them */ + (opline + 1)->opcode = ZEND_JMP; + ZEND_OP1(opline + 1).opline_num = last_op - op_array->opcodes; /* that's OK even for ZE2, since opline_num's are resolved in pass 2 later */ + } + } + } + break; + + case ZEND_FETCH_CONSTANT: + if (ZEND_OP1_TYPE(opline) == IS_UNUSED && + ZEND_OP2_TYPE(opline) == IS_CONST && + Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING && + Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__") - 1 && + memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1) == 0) { + /* substitute __COMPILER_HALT_OFFSET__ constant */ + zend_bool orig_in_execution = EG(in_execution); + zend_op_array *orig_op_array = EG(active_op_array); + zval offset; + + EG(in_execution) = 1; + EG(active_op_array) = op_array; + if (zend_get_constant("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1, &offset TSRMLS_CC)) { + literal_dtor(&ZEND_OP2_LITERAL(opline)); + ZEND_OP1_TYPE(opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + opline->op1.constant = zend_optimizer_add_literal(op_array, &offset TSRMLS_CC); +#else + ZEND_OP1_LITERAL(opline) = offset; +#endif + SET_UNUSED(opline->op2); + opline->opcode = ZEND_QM_ASSIGN; + } + EG(active_op_array) = orig_op_array; + EG(in_execution) = orig_in_execution; + break; + } + + if (ZEND_OP1_TYPE(opline) == IS_UNUSED && + ZEND_OP2_TYPE(opline) == IS_CONST && + ZEND_OP2_LITERAL(opline).type == IS_STRING) { + /* substitute persistent constants */ + zval c; + + if (!zend_get_persistent_constant(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), &c, 1 TSRMLS_CC)) { + break; + } + literal_dtor(&ZEND_OP2_LITERAL(opline)); + ZEND_OP1_TYPE(opline) = IS_CONST; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + opline->op1.constant = zend_optimizer_add_literal(op_array, &c TSRMLS_CC); +#else + ZEND_OP1_LITERAL(opline) = c; +#endif + SET_UNUSED(opline->op2); + opline->opcode = ZEND_QM_ASSIGN; + } + break; + + case ZEND_INIT_FCALL_BY_NAME: + if (opline->extended_value == 0 /* not method */ && + ZEND_OP1_TYPE(opline) == IS_UNUSED && + ZEND_OP2_TYPE(opline) == IS_CONST) { + if ((opline + 1)->opcode == ZEND_DO_FCALL_BY_NAME && + (opline + 1)->extended_value == 0) { + (opline + 1)->opcode = ZEND_DO_FCALL; + COPY_NODE((opline + 1)->op1, opline->op2); + zend_str_tolower(Z_STRVAL(ZEND_OP1_LITERAL(opline + 1)), Z_STRLEN(ZEND_OP1_LITERAL(opline + 1))); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + Z_HASH_P(&ZEND_OP1_LITERAL(opline + 1)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline + 1)), Z_STRLEN(ZEND_OP1_LITERAL(opline + 1)) + 1); + op_array->literals[(opline + 1)->op1.constant].cache_slot = op_array->last_cache_slot++; +#endif + MAKE_NOP(opline); + } + } + break; + } + opline++; + i++; + } +} diff --git a/ext/opcache/Optimizer/pass2.c b/ext/opcache/Optimizer/pass2.c new file mode 100644 index 000000000..30708a093 --- /dev/null +++ b/ext/opcache/Optimizer/pass2.c @@ -0,0 +1,211 @@ +/* pass 2: + * - convert non-numeric constants to numeric constants in numeric operators + * - optimize constant conditional JMPs + * - optimize static BRKs and CONTs + */ + +if (ZEND_OPTIMIZER_PASS_2 & OPTIMIZATION_LEVEL) { + zend_op *opline; + zend_op *end = op_array->opcodes + op_array->last; + + opline = op_array->opcodes; + while (opline < end) { + switch (opline->opcode) { + case ZEND_ADD: + case ZEND_SUB: + case ZEND_MUL: + case ZEND_DIV: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + if (ZEND_OP1_LITERAL(opline).type == IS_STRING) { + convert_scalar_to_number(&ZEND_OP1_LITERAL(opline) TSRMLS_CC); + } + } + /* break missing *intentionally* - the assign_op's may only optimize op2 */ + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + if (opline->extended_value != 0) { + /* object tristate op - don't attempt to optimize it! */ + break; + } + if (ZEND_OP2_TYPE(opline) == IS_CONST) { + if (ZEND_OP2_LITERAL(opline).type == IS_STRING) { + convert_scalar_to_number(&ZEND_OP2_LITERAL(opline) TSRMLS_CC); + } + } + break; + + case ZEND_MOD: + case ZEND_SL: + case ZEND_SR: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + if (ZEND_OP1_LITERAL(opline).type != IS_LONG) { + convert_to_long(&ZEND_OP1_LITERAL(opline)); + } + } + /* break missing *intentionally - the assign_op's may only optimize op2 */ + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + if (opline->extended_value != 0) { + /* object tristate op - don't attempt to optimize it! */ + break; + } + if (ZEND_OP2_TYPE(opline) == IS_CONST) { + if (ZEND_OP2_LITERAL(opline).type != IS_LONG) { + convert_to_long(&ZEND_OP2_LITERAL(opline)); + } + } + break; + + case ZEND_CONCAT: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + if (ZEND_OP1_LITERAL(opline).type != IS_STRING) { + convert_to_string(&ZEND_OP1_LITERAL(opline)); + } + } + /* break missing *intentionally - the assign_op's may only optimize op2 */ + case ZEND_ASSIGN_CONCAT: + if (opline->extended_value != 0) { + /* object tristate op - don't attempt to optimize it! */ + break; + } + if (ZEND_OP2_TYPE(opline) == IS_CONST) { + if (ZEND_OP2_LITERAL(opline).type != IS_STRING) { + convert_to_string(&ZEND_OP2_LITERAL(opline)); + } + } + break; + + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + /* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */ + if (0 && /* FIXME: temporary disable unsafe pattern */ + ZEND_OP1_TYPE(opline) == IS_TMP_VAR && + ZEND_RESULT_TYPE(opline) == IS_TMP_VAR && + ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { + opline->opcode -= 3; + /* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C) + in case we know it wouldn't jump */ + } else if (ZEND_OP1_TYPE(opline) == IS_CONST) { + int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); + if (opline->opcode == ZEND_JMPZ_EX) { + should_jmp = !should_jmp; + } + if (!should_jmp) { + opline->opcode = ZEND_QM_ASSIGN; + SET_UNUSED(opline->op2); + } + } + break; + + case ZEND_JMPZ: + case ZEND_JMPNZ: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline)); + + if (opline->opcode == ZEND_JMPZ) { + should_jmp = !should_jmp; + } + literal_dtor(&ZEND_OP1_LITERAL(opline)); + ZEND_OP1_TYPE(opline) = IS_UNUSED; + if (should_jmp) { + opline->opcode = ZEND_JMP; + COPY_NODE(opline->op1, opline->op2); + } else { + MAKE_NOP(opline); + } + break; + } + if ((opline + 1)->opcode == ZEND_JMP) { + /* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */ + /* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */ + if (ZEND_OP2(opline).opline_num == ZEND_OP1(opline + 1).opline_num) { + /* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */ + MAKE_NOP(opline); + } else { + if (opline->opcode == ZEND_JMPZ) { + opline->extended_value = ZEND_OP1(opline + 1).opline_num; + } else { + opline->extended_value = ZEND_OP2(opline).opline_num; + COPY_NODE(opline->op2, (opline + 1)->op1); + } + opline->opcode = ZEND_JMPZNZ; + } + } + break; + + case ZEND_JMPZNZ: + if (ZEND_OP1_TYPE(opline) == IS_CONST) { + int opline_num; + + if (zend_is_true(&ZEND_OP1_LITERAL(opline))) { + opline_num = opline->extended_value; /* JMPNZ */ + } else { + opline_num = ZEND_OP2(opline).opline_num; /* JMPZ */ + } + literal_dtor(&ZEND_OP1_LITERAL(opline)); + ZEND_OP1(opline).opline_num = opline_num; + ZEND_OP1_TYPE(opline) = IS_UNUSED; + opline->opcode = ZEND_JMP; + } + break; + + case ZEND_BRK: + case ZEND_CONT: + { + zend_brk_cont_element *jmp_to; + int array_offset; + int nest_levels; + int dont_optimize = 0; + + if (ZEND_OP2_TYPE(opline) != IS_CONST) { + break; + } + convert_to_long(&ZEND_OP2_LITERAL(opline)); + nest_levels = ZEND_OP2_LITERAL(opline).value.lval; + + array_offset = ZEND_OP1(opline).opline_num; + while (1) { + if (array_offset == -1) { + dont_optimize = 1; /* don't optimize this bogus break/continue, let the executor shout */ + break; + } + jmp_to = &op_array->brk_cont_array[array_offset]; + array_offset = jmp_to->parent; + if (--nest_levels > 0) { + if (opline->opcode == ZEND_BRK && + (op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE || + op_array->opcodes[jmp_to->brk].opcode == ZEND_SWITCH_FREE)) { + dont_optimize = 1; + break; + } + } else { + break; + } + } + + if (dont_optimize) { + break; + } + + /* optimize - convert to a JMP */ + switch (opline->opcode) { + case ZEND_BRK: + MAKE_NOP(opline); + ZEND_OP1(opline).opline_num = jmp_to->brk; + break; + case ZEND_CONT: + MAKE_NOP(opline); + ZEND_OP1(opline).opline_num = jmp_to->cont; + break; + } + opline->opcode = ZEND_JMP; + /* MAKE_NOP() already set op1 and op2 to IS_UNUSED */ + } + break; + } + opline++; + } +} diff --git a/ext/opcache/Optimizer/pass3.c b/ext/opcache/Optimizer/pass3.c new file mode 100644 index 000000000..fd2a19000 --- /dev/null +++ b/ext/opcache/Optimizer/pass3.c @@ -0,0 +1,442 @@ +/* pass 3: + * - optimize $i = $i+expr to $i+=expr + * - optimize series of JMPs + * - change $i++ to ++$i where possible + */ + +/* compares opcodes with allowing oc1 be _EX of oc2 */ +#define SAME_OPCODE_EX(oc1, oc2) ((oc1 == oc2) || (oc1 == ZEND_JMPZ_EX && oc2 == ZEND_JMPZ) || (oc1 == ZEND_JMPNZ_EX && oc2 == ZEND_JMPNZ)) + +/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */ +#define CHECK_JMP(target, label) \ + for (i=0; i<jmp_hitlist_count; i++) { \ + if (jmp_hitlist[i] == ZEND_OP1(&op_array->opcodes[target]).opline_num) { \ + goto label; \ + } \ + } \ + jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1(&op_array->opcodes[target]).opline_num; + +#define CHECK_JMP2(target, label) \ + for (i=0; i<jmp_hitlist_count; i++) { \ + if (jmp_hitlist[i] == ZEND_OP2(&op_array->opcodes[target]).opline_num) { \ + goto label; \ + } \ + } \ + jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2(&op_array->opcodes[target]).opline_num; + +if (ZEND_OPTIMIZER_PASS_3 & OPTIMIZATION_LEVEL) { + zend_op *opline; + zend_op *end = op_array->opcodes + op_array->last; + zend_uint *jmp_hitlist; + int jmp_hitlist_count; + int i; + zend_uint opline_num = 0; + ALLOCA_FLAG(use_heap); + + jmp_hitlist = (zend_uint *)DO_ALLOCA(sizeof(zend_uint)*op_array->last); + opline = op_array->opcodes; + + while (opline < end) { + jmp_hitlist_count = 0; + + switch (opline->opcode) { + case ZEND_ADD: + case ZEND_SUB: + case ZEND_MUL: + case ZEND_DIV: + case ZEND_MOD: + case ZEND_CONCAT: + case ZEND_SL: + case ZEND_SR: + case ZEND_BW_OR: + case ZEND_BW_AND: + case ZEND_BW_XOR: + { + zend_op *next_opline = opline + 1; + + while (next_opline < end && next_opline->opcode == ZEND_NOP) { + ++next_opline; + } + + if (next_opline >= end || next_opline->opcode != ZEND_ASSIGN) { + break; + } + + if ((ZEND_OP2_TYPE(opline) == IS_VAR || ZEND_OP2_TYPE(opline) == IS_CV) + && ZEND_OP2(opline).var == ZEND_OP1(next_opline).var && + (opline->opcode == ZEND_ADD || + opline->opcode == ZEND_MUL || + opline->opcode == ZEND_BW_OR || + opline->opcode == ZEND_BW_AND || + opline->opcode == ZEND_BW_XOR)) { + /* change $i=expr+$i to $i=$i+expr so that the next + * optimization works on it + */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + zend_uchar tmp_type = opline->op1_type; + znode_op tmp = opline->op1; +#else + znode tmp = opline->op1; +#endif + + if (opline->opcode != ZEND_ADD || ZEND_OP1_TYPE(opline) == IS_CONST) { + /* protection from array add: $a = array + $a is not commutative! */ + COPY_NODE(opline->op1, opline->op2); + COPY_NODE(opline->op2, tmp); + } + } + if ((ZEND_OP1_TYPE(opline) == IS_VAR || ZEND_OP1_TYPE(opline) == IS_CV) + && ZEND_OP1(opline).var == ZEND_OP1(next_opline).var + && ZEND_OP1_TYPE(opline) == ZEND_OP1_TYPE(next_opline)) { + switch (opline->opcode) { + case ZEND_ADD: + opline->opcode = ZEND_ASSIGN_ADD; + break; + case ZEND_SUB: + opline->opcode = ZEND_ASSIGN_SUB; + break; + case ZEND_MUL: + opline->opcode = ZEND_ASSIGN_MUL; + break; + case ZEND_DIV: + opline->opcode = ZEND_ASSIGN_DIV; + break; + case ZEND_MOD: + opline->opcode = ZEND_ASSIGN_MOD; + break; + case ZEND_CONCAT: + opline->opcode = ZEND_ASSIGN_CONCAT; + break; + case ZEND_SL: + opline->opcode = ZEND_ASSIGN_SL; + break; + case ZEND_SR: + opline->opcode = ZEND_ASSIGN_SR; + break; + case ZEND_BW_OR: + opline->opcode = ZEND_ASSIGN_BW_OR; + break; + case ZEND_BW_AND: + opline->opcode = ZEND_ASSIGN_BW_AND; + break; + case ZEND_BW_XOR: + opline->opcode = ZEND_ASSIGN_BW_XOR; + break; + } + COPY_NODE(opline->result, next_opline->result); + MAKE_NOP(next_opline); + opline++; + opline_num++; + } + } + break; + + case ZEND_JMP: +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + + /* convert L: JMP L+1 to NOP */ + if (ZEND_OP1(opline).opline_num == opline_num + 1) { + MAKE_NOP(opline); + goto done_jmp_optimization; + } + + /* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */ + while (ZEND_OP1(opline).opline_num < op_array->last + && op_array->opcodes[ZEND_OP1(opline).opline_num].opcode == ZEND_JMP) { + int target = ZEND_OP1(opline).opline_num; + CHECK_JMP(target, done_jmp_optimization); + ZEND_OP1(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } + break; + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_JMP_SET: +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + + while (ZEND_OP2(opline).opline_num < op_array->last) { + int target = ZEND_OP2(opline).opline_num; + if (op_array->opcodes[target].opcode == ZEND_JMP) { + ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } else { + break; + } + } + break; +#endif + + case ZEND_JMPZ: + case ZEND_JMPNZ: +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + + /* convert L: JMPZ L+1 to NOP */ + if (ZEND_OP2(opline).opline_num == opline_num + 1) { + MAKE_NOP(opline); + goto done_jmp_optimization; + } + + while (ZEND_OP2(opline).opline_num < op_array->last) { + int target = ZEND_OP2(opline).opline_num; + + if (op_array->opcodes[target].opcode == ZEND_JMP) { + /* plain JMP */ + /* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */ + CHECK_JMP(target, done_jmp_optimization); + ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } else if (op_array->opcodes[target].opcode == opline->opcode && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* same opcode and same var as this opcode */ + /* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */ + CHECK_JMP2(target, done_jmp_optimization); + ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num; + } else if (op_array->opcodes[target].opcode == opline->opcode + 3 && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to + T = JMPZ_EX(X, L2) */ + ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;opline->opcode += 3; + COPY_NODE(opline->result, op_array->opcodes[target].result); + break; + } else if (op_array->opcodes[target].opcode == INV_COND(opline->opcode) && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to + JMPZ(X,L1+1) */ + ZEND_OP2(opline).opline_num = target + 1; + break; + } else if (op_array->opcodes[target].opcode == INV_COND_EX(opline->opcode) && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to + T = JMPZ_EX(X,L1+1) */ + ZEND_OP2(opline).opline_num = target + 1; + opline->opcode += 3; + COPY_NODE(opline->result, op_array->opcodes[target].result); + break; + } else { + break; + } + } + break; + + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: { +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + zend_uchar T_type = opline->result_type; + znode_op T = opline->result; +#else + znode T = opline->result; +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + /* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */ + /* convert L: T = JMPZ_EX T,L+1 to NOP */ + if (ZEND_OP2(opline).opline_num == opline_num + 1) { + if (ZEND_OP1(opline).var == ZEND_RESULT(opline).var) { + MAKE_NOP(opline); + } else { + opline->opcode = ZEND_BOOL; + SET_UNUSED(opline->op2); + } + goto done_jmp_optimization; + } + + while (ZEND_OP2(opline).opline_num < op_array->last) { + int target = ZEND_OP2(opline).opline_num; + if (SAME_OPCODE_EX(opline->opcode, op_array->opcodes[target].opcode) && + SAME_VAR(op_array->opcodes[target].op1, T)) { + /* Check for JMPZ_EX to JMPZ[_EX] with the same condition, either with _EX or not */ + if (op_array->opcodes[target].opcode == opline->opcode) { + /* change T only if we have _EX opcode there */ + COPY_NODE(T, op_array->opcodes[target].result); + } + CHECK_JMP2(target, continue_jmp_ex_optimization); + ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num; + } else if (op_array->opcodes[target].opcode == ZEND_JMPZNZ && + SAME_VAR(op_array->opcodes[target].op1, T)) { + /* Check for JMPZNZ with same cond variable */ + int new_target; + CHECK_JMP2(target, continue_jmp_ex_optimization); + if (opline->opcode == ZEND_JMPZ_EX) { + new_target = ZEND_OP2(&op_array->opcodes[target]).opline_num; + } else { + /* JMPNZ_EX */ + new_target = op_array->opcodes[target].extended_value; + } + ZEND_OP2(opline).opline_num = new_target; + } else if ((op_array->opcodes[target].opcode == INV_EX_COND_EX(opline->opcode) || + op_array->opcodes[target].opcode == INV_EX_COND(opline->opcode)) && + SAME_VAR(opline->op1, op_array->opcodes[target].op1)) { + /* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to + JMPZ_EX(X,L1+1) */ + ZEND_OP2(opline).opline_num = target + 1; + break; + } else { + break; + } + } /* while */ +continue_jmp_ex_optimization: + break; +#if 0 + /* If Ti = JMPZ_EX(X, L) and Ti is not used, convert to JMPZ(X, L) */ + { + zend_op *op; + for(op = opline+1; op<end; op++) { + if(ZEND_RESULT_TYPE(op) == IS_TMP_VAR && + ZEND_RESULT(op).var == ZEND_RESULT(opline).var) { + break; /* can pass to part 2 */ + } + + if(op->opcode == ZEND_JMP || + op->opcode == ZEND_JMPZ || + op->opcode == ZEND_JMPZ_EX || + op->opcode == ZEND_JMPNZ || + op->opcode == ZEND_JMPNZ_EX || + op->opcode == ZEND_JMPZNZ || + op->opcode == ZEND_BRK || + op->opcode == ZEND_CONT || + op->opcode == ZEND_CASE || + op->opcode == ZEND_RETURN || +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + op->opcode == ZEND_RETURN_BY_REF || +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + op->opcode == ZEND_FAST_RET || +#endif + op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_EXIT) { + break; + } + + if(ZEND_OP1_TYPE(op) == IS_TMP_VAR && + ZEND_OP1(op).var == ZEND_RESULT(opline).var) { + goto done_jmp_optimization; + } + + if(ZEND_OP2_TYPE(op) == IS_TMP_VAR && + ZEND_OP2(op).var == ZEND_RESULT(opline).var) { + goto done_jmp_optimization; + } + } /* for */ + + for(op = &op_array->opcodes[ZEND_OP2(opline).opline_num]; op<end; op++) { + + if(ZEND_RESULT_TYPE(op) == IS_TMP_VAR && + ZEND_RESULT(op).var == ZEND_RESULT(opline).var) { + break; /* can pass to optimization */ + } + + if(op->opcode == ZEND_JMP || + op->opcode == ZEND_JMPZ || + op->opcode == ZEND_JMPZ_EX || + op->opcode == ZEND_JMPNZ || + op->opcode == ZEND_JMPNZ_EX || + op->opcode == ZEND_JMPZNZ || + op->opcode == ZEND_BRK || + op->opcode == ZEND_CONT || + op->opcode == ZEND_CASE || + op->opcode == ZEND_RETURN || +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + op->opcode == ZEND_RETURN_BY_REF || +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + op->opcode == ZEND_FAST_RET || +#endif + op->opcode == ZEND_FE_FETCH || + op->opcode == ZEND_EXIT) { + break; + } + + if(ZEND_OP1_TYPE(op) == IS_TMP_VAR && + ZEND_OP1(op).var == ZEND_RESULT(opline).var) { + goto done_jmp_optimization; + } + + if(ZEND_OP2_TYPE(op) == IS_TMP_VAR && + ZEND_OP2(op).var == ZEND_RESULT(opline).var) { + goto done_jmp_optimization; + } + } + + opline->opcode = opline->opcode-3; /* JMP_EX -> JMP */ + SET_UNUSED(opline->result); + break; + } +#endif + } + break; + + case ZEND_JMPZNZ: +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + if (op_array->has_finally_block) { + break; + } +#endif + /* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */ + while (ZEND_OP2(opline).opline_num < op_array->last + && op_array->opcodes[ZEND_OP2(opline).opline_num].opcode == ZEND_JMP) { + int target = ZEND_OP2(opline).opline_num; + CHECK_JMP(target, continue_jmpznz_optimization); + ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } +continue_jmpznz_optimization: + /* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */ + while (opline->extended_value < op_array->last + && op_array->opcodes[opline->extended_value].opcode == ZEND_JMP) { + int target = opline->extended_value; + CHECK_JMP(target, done_jmp_optimization); + opline->extended_value = ZEND_OP1(&op_array->opcodes[target]).opline_num; + } + break; + + case ZEND_POST_INC: + case ZEND_POST_DEC: { + /* POST_INC, FREE => PRE_INC */ + zend_op *next_op = opline + 1; + + if (next_op >= end) { + break; + } + if (next_op->opcode == ZEND_FREE && + ZEND_OP1(next_op).var == ZEND_RESULT(opline).var) { + MAKE_NOP(next_op); + switch (opline->opcode) { + case ZEND_POST_INC: + opline->opcode = ZEND_PRE_INC; + break; + case ZEND_POST_DEC: + opline->opcode = ZEND_PRE_DEC; + break; + } +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ZEND_RESULT_TYPE(opline) = IS_VAR | EXT_TYPE_UNUSED; +#else + ZEND_RESULT_TYPE(opline) = IS_VAR; + ZEND_RESULT(opline).EA.type = 0; + ZEND_RESULT(opline).EA.type |= EXT_TYPE_UNUSED; +#endif + } + } + break; + } +done_jmp_optimization: + opline++; + opline_num++; + } + FREE_ALLOCA(jmp_hitlist); +} diff --git a/ext/opcache/Optimizer/pass5.c b/ext/opcache/Optimizer/pass5.c new file mode 100644 index 000000000..b0d651a5f --- /dev/null +++ b/ext/opcache/Optimizer/pass5.c @@ -0,0 +1,3 @@ +if (ZEND_OPTIMIZER_PASS_5 & OPTIMIZATION_LEVEL) { + zend_block_optimization(op_array TSRMLS_CC); +} diff --git a/ext/opcache/Optimizer/pass9.c b/ext/opcache/Optimizer/pass9.c new file mode 100644 index 000000000..586160c14 --- /dev/null +++ b/ext/opcache/Optimizer/pass9.c @@ -0,0 +1,8 @@ +/* pass 9 + * + * - optimize usage of temporary variables + */ + +if (ZEND_OPTIMIZER_PASS_9 & OPTIMIZATION_LEVEL) { + optimize_temporary_variables(op_array); +} diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c new file mode 100644 index 000000000..b574ecc81 --- /dev/null +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -0,0 +1,139 @@ +/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@zend.com> |
+ | Zeev Suraski <zeev@zend.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "Optimizer/zend_optimizer.h"
+#include "Optimizer/zend_optimizer_internal.h"
+#include "zend_API.h"
+#include "zend_constants.h"
+#include "zend_execute.h"
+
+#define OPTIMIZATION_LEVEL \
+ ZCG(accel_directives).optimization_level
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+int zend_optimizer_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC)
+{
+ int i = op_array->last_literal;
+ op_array->last_literal++;
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+ {
+ if (i >= CG(context).literals_size) {
+ CG(context).literals_size += 16; /* FIXME */
+ op_array->literals = (zend_literal*)erealloc(op_array->literals, CG(context).literals_size * sizeof(zend_literal));
+ }
+ }
+#else
+ if (i >= op_array->size_literal) {
+ op_array->size_literal += 16; /* FIXME */
+ op_array->literals = (zend_literal*)erealloc(op_array->literals, op_array->size_literal * sizeof(zend_literal));
+ }
+#endif
+ op_array->literals[i].constant = *zv;
+ Z_SET_REFCOUNT(op_array->literals[i].constant, 2);
+ Z_SET_ISREF(op_array->literals[i].constant);
+ return i;
+}
+
+# define LITERAL_LONG(op, val) do { \
+ zval _c; \
+ ZVAL_LONG(&_c, val); \
+ op.constant = zend_optimizer_add_literal(op_array, &_c TSRMLS_CC); \
+ } while (0)
+
+# define LITERAL_BOOL(op, val) do { \
+ zval _c; \
+ ZVAL_BOOL(&_c, val); \
+ op.constant = zend_optimizer_add_literal(op_array, &_c TSRMLS_CC); \
+ } while (0)
+
+# define literal_dtor(zv) do { \
+ zval_dtor(zv); \
+ Z_TYPE_P(zv) = IS_NULL; \
+ } while (0)
+
+#define COPY_NODE(target, src) do { \
+ target ## _type = src ## _type; \
+ target = src; \
+ } while (0)
+
+#else
+
+# define LITERAL_LONG(op, val) ZVAL_LONG(&op.u.constant, val)
+
+# define LITERAL_BOOL(op, val) ZVAL_BOOL(&op.u.constant, val)
+
+# define literal_dtor(zv) zval_dtor(zv)
+
+#define COPY_NODE(target, src) do { \
+ target = src; \
+ } while (0)
+
+#endif
+
+#include "Optimizer/nop_removal.c"
+#include "Optimizer/block_pass.c"
+#include "Optimizer/optimize_temp_vars_5.c"
+
+void zend_optimizer(zend_op_array *op_array TSRMLS_DC)
+{
+ if (op_array->type == ZEND_EVAL_CODE ||
+ (op_array->fn_flags & ZEND_ACC_INTERACTIVE)) {
+ return;
+ }
+
+ /* pass 1
+ * - substitute persistent constants (true, false, null, etc)
+ * - perform compile-time evaluation of constant binary and unary operations
+ * - optimize series of ADD_STRING and/or ADD_CHAR
+ * - convert CAST(IS_BOOL,x) into BOOL(x)
+ * - convert INTI_FCALL_BY_NAME + DO_FCALL_BY_NAME into DO_FCALL
+ */
+#include "Optimizer/pass1_5.c"
+
+ /* pass 2:
+ * - convert non-numeric constants to numeric constants in numeric operators
+ * - optimize constant conditional JMPs
+ * - optimize static BRKs and CONTs
+ */
+#include "Optimizer/pass2.c"
+
+ /* pass 3:
+ * - optimize $i = $i+expr to $i+=expr
+ * - optimize series of JMPs
+ * - change $i++ to ++$i where possible
+ */
+#include "Optimizer/pass3.c"
+
+ /* pass 5:
+ * - CFG optimization
+ */
+#include "Optimizer/pass5.c"
+
+ /* pass 9:
+ * - Optimize temp variables usage
+ */
+#include "Optimizer/pass9.c"
+
+ /* pass 10:
+ * - remove NOPs
+ */
+#include "Optimizer/pass10.c"
+}
diff --git a/ext/opcache/Optimizer/zend_optimizer.h b/ext/opcache/Optimizer/zend_optimizer.h new file mode 100644 index 000000000..98275a20a --- /dev/null +++ b/ext/opcache/Optimizer/zend_optimizer.h @@ -0,0 +1,49 @@ +/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@zend.com> |
+ | Zeev Suraski <zeev@zend.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_OPTIMIZER_H
+#define ZEND_OPTIMIZER_H
+
+#include "zend.h"
+#include "zend_compile.h"
+
+#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* CSE, STRING construction */
+#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* Constant conversion and jumps */
+#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* ++, +=, series of jumps */
+#define ZEND_OPTIMIZER_PASS_4 (1<<3)
+#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
+#define ZEND_OPTIMIZER_PASS_6 (1<<5)
+#define ZEND_OPTIMIZER_PASS_7 (1<<6)
+#define ZEND_OPTIMIZER_PASS_8 (1<<7)
+#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */
+#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */
+#define ZEND_OPTIMIZER_PASS_11 (1<<10)
+#define ZEND_OPTIMIZER_PASS_12 (1<<11)
+#define ZEND_OPTIMIZER_PASS_13 (1<<12)
+#define ZEND_OPTIMIZER_PASS_14 (1<<13)
+
+#define ZEND_OPTIMIZER_ALL_PASSES 0xFFFFFFFF
+
+#define DEFAULT_OPTIMIZATION_LEVEL "0xFFFFFFFF"
+
+void zend_optimizer(zend_op_array *op_array TSRMLS_DC);
+
+#endif
diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h new file mode 100644 index 000000000..0097496df --- /dev/null +++ b/ext/opcache/Optimizer/zend_optimizer_internal.h @@ -0,0 +1,83 @@ +/*
+ +----------------------------------------------------------------------+
+ | Zend OPcache |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andi Gutmans <andi@zend.com> |
+ | Zeev Suraski <zeev@zend.com> |
+ | Stanislav Malyshev <stas@zend.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef ZEND_OPTIMIZER_INTERNAL_H
+#define ZEND_OPTIMIZER_INTERNAL_H
+
+#include "ZendAccelerator.h"
+
+#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
+# define VAR_NUM(v) ((zend_uint)(EX_TMP_VAR_NUM(0, 0) - EX_TMP_VAR(0, v)))
+# define NUM_VAR(v) ((zend_uint)EX_TMP_VAR_NUM(0, v))
+#else
+# define VAR_NUM(v) ((v)/(sizeof(temp_variable)))
+# define NUM_VAR(v) ((v)*(sizeof(temp_variable)))
+#endif
+
+#define INV_COND(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ : ZEND_JMPZ)
+#define INV_EX_COND(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ : ZEND_JMPZ)
+#define INV_COND_EX(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
+#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
+
+#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
+# define MAKE_NOP(opline) { opline->opcode = ZEND_NOP; memset(&opline->result,0,sizeof(opline->result)); memset(&opline->op1,0,sizeof(opline->op1)); memset(&opline->op2,0,sizeof(opline->op2)); opline->result_type=opline->op1_type=opline->op2_type=IS_UNUSED; opline->handler = zend_opcode_handlers[ZEND_NOP]; }
+# define RESULT_USED(op) (((op->result_type & IS_VAR) && !(op->result_type & EXT_TYPE_UNUSED)) || op->result_type == IS_TMP_VAR)
+# define RESULT_UNUSED(op) ((op->result_type & EXT_TYPE_UNUSED) != 0)
+# define SAME_VAR(op1, op2) ((((op1 ## _type & IS_VAR) && (op2 ## _type & IS_VAR)) || (op1 ## _type == IS_TMP_VAR && op2 ## _type == IS_TMP_VAR)) && op1.var == op2.var)
+#else
+# define MAKE_NOP(opline) { opline->opcode = ZEND_NOP; memset(&opline->result,0,sizeof(znode)); memset(&opline->op1,0,sizeof(znode)); memset(&opline->op2,0,sizeof(znode)); opline->result.op_type=opline->op1.op_type=opline->op2.op_type=IS_UNUSED; opline->handler = zend_opcode_handlers[ZEND_NOP]; }
+# define RESULT_USED(op) ((op->result.op_type == IS_VAR && (op->result.u.EA.type & EXT_TYPE_UNUSED) == 0) || (op->result.op_type == IS_TMP_VAR))
+# define RESULT_UNUSED(op) ((op->result.op_type == IS_VAR) && (op->result.u.EA.type == EXT_TYPE_UNUSED))
+# define SAME_VAR(op1, op2) (((op1.op_type == IS_VAR && op2.op_type == IS_VAR) || (op1.op_type == IS_TMP_VAR && op2.op_type == IS_TMP_VAR)) && op1.u.var == op2.u.var)
+#endif
+
+typedef struct _zend_code_block zend_code_block;
+typedef struct _zend_block_source zend_block_source;
+
+struct _zend_code_block {
+ int access;
+ zend_op *start_opline;
+ int start_opline_no;
+ int len;
+ zend_code_block *op1_to;
+ zend_code_block *op2_to;
+ zend_code_block *ext_to;
+ zend_code_block *follow_to;
+ zend_code_block *next;
+ zend_block_source *sources;
+ zend_bool protected; /* don't merge this block with others */
+};
+
+typedef struct _zend_cfg {
+ zend_code_block *blocks;
+ zend_code_block **try;
+ zend_code_block **catch;
+ zend_code_block **loop_start;
+ zend_code_block **loop_cont;
+ zend_code_block **loop_brk;
+} zend_cfg;
+
+struct _zend_block_source {
+ zend_code_block *from;
+ zend_block_source *next;
+};
+
+#endif
diff --git a/ext/opcache/README b/ext/opcache/README new file mode 100644 index 000000000..f87e1ccbf --- /dev/null +++ b/ext/opcache/README @@ -0,0 +1,202 @@ +The Zend OPcache +================ + +The Zend OPcache provides faster PHP execution through opcode caching and +optimization. It improves PHP performance by storing precompiled script +bytecode in the shared memory. This eliminates the stages of reading code from +the disk and compiling it on future access. In addition, it applies a few +bytecode optimization patterns that make code execution faster. + +Compatibility +------------- + +This version of Zend OPcache is compatible with PHP 5.2.*, 5.3.*, 5.4.* +and PHP-5.5 development branch. PHP 5.2 support may be removed in the future. + +Quick Install +------------- + +- Compile + + $PHP_DIR/bin/phpize + ./configure \ + --with-php-config=$PHP_DIR/bin/php-config + make + +- Install + + make install # this will copy opcache.so into PHP extension directory + +- Edit php.ini + + zend_extension=/...full path.../opcache.so + +NOTE: In case you are going to use Zend OPcache together with Xdebug, +be sure that Xdebug is loaded after OPcache. "php -v" must show Xdebug +after OPcache. + +- Restart PHP + +Speed Tuning +------------- + +We recommend the following configuration options for best performance. + +opcache.memory_consumption=128 +opcache.interned_strings_buffer=8 +opcache.max_accelerated_files=4000 +opcache.revalidate_freq=60 +opcache.fast_shutdown=1 +opcache.enable_cli=1 + +You also may add the following, but it may break some applications and +frameworks. Please, read description of these directives and add them on your +own risk. + +opcache.save_comments=0 +opcache.enable_file_override=1 + +In some cases you may like to prefer enabling/disabling some features +to avoid incompatibilities at the cost of some performance degradation. + +Configuration Directives +------------------------ + +opcache.enable (default "1") + OPcache On/Off switch. When set to Off, code is not optimized. + +opcache.memory_consumption (default "64") + The OPcache shared memory storage size. The amount of memory for storing + precompiled PHP code in Mbytes. + +opcache.interned_strings_buffer (default "4") + The amount of memory for interned strings in Mbytes. + +opcache.max_accelerated_files (default "2000") + The maximum number of keys (scripts) in the OPcache hash table. + The number is actually the first one in the following set of prime + numbers that is bigger than the one supplied: { 223, 463, 983, 1979, 3907, + 7963, 16229, 32531, 65407, 130987 }. Only numbers between 200 and 100000 + are allowed. + +opcache.max_wasted_percentage (default "5") + The maximum percentage of "wasted" memory until a restart is scheduled. + +opcache.use_cwd (default "1") + When this directive is enabled, the OPcache appends the current working + directory to the script key, thus eliminating possible collisions between + files with the same name (basename). Disabling the directive improves + performance, but may break existing applications. + +opcache.validate_timestamps (default "1") + When disabled, you must reset the OPcache manually or restart the + webserver for changes to the filesystem to take effect. + The frequency of the check is controlled by the directive + "opcache.revalidate_freq". + +opcache.revalidate_freq (default "2") + How often (in seconds) to check file timestamps for changes to the shared + memory storage allocation. ("1" means validate once per second, but only + once per request. "0" means always validate) + +opcache.revalidate_path (default "0") + Enables or disables file search in include_path optimization + If the file search is disabled and a cached file is found that uses + the same include_path, the file is not searched again. Thus, if a file + with the same name appears somewhere else in include_path, it + won't be found. Enable this directive if this optimization has an effect on + your applications. The default for this directive is disabled, which means + that optimization is active. + +opcache.save_comments (default "1") + If disabled, all PHPDoc comments are dropped from the code to reduce the + size of the optimized code. Disabling "Doc Comments" may break some + existing applications and frameworks (e.g. Doctrine, ZF2, PHPUnit) + +opcache.load_comments (default "1") + If disabled, PHPDoc comments are not loaded from SHM, so "Doc Comments" + may be always stored (save_comments=1), but not loaded by applications + that don't need them anyway. + +opcache.fast_shutdown (default "0") + If enabled, a fast shutdown sequence is used for the accelerated code + The fast shutdown sequence doesn't free each allocated block, but lets + the Zend Engine Memory Manager do the work. + +opcache.enable_file_override (default "0") + Allow file existence override (file_exists, etc.) performance feature. + +opcache.optimization_level (default "0xffffffff") + A bitmask, where each bit enables or disables the appropriate OPcache + passes + +opcache.inherited_hack (default "1") + Enable this hack as a workaround for "can't redeclare class" errors. + The OPcache stores the places where DECLARE_CLASS opcodes use + inheritance (These are the only opcodes that can be executed by PHP, + but which may not be executed because the parent class is missing due to + optimization). When the file is loaded, OPcache tries to bind the + inherited classes by using the current environment. The problem with this + scenario is that, while the DECLARE_CLASS opcode may not be needed for the + current script, if the script requires that the opcode at least be defined, + it may not run. The default for this directive is disabled, which means + that optimization is active. In php-5.3 and above this hack is not needed + anymore and this setting has no effect. + +opcache.dups_fix (default "0") + Enable this hack as a workaround for "Cannot redeclare class" errors. + +opcache.blacklist_filename + The location of the OPcache blacklist file. + The OPcache blacklist file is a text file that holds the names of files + that should not be accelerated. The file format is to add each filename + to a new line. The filename may be a full path or just a file prefix + (i.e., /var/www/x blacklists all the files and directories in /var/www + that start with 'x'). Files are usually triggered by one of the following + three reasons: + 1) Directories that contain auto generated code, like Smarty or ZFW cache. + 2) Code that does not work well when accelerated, due to some delayed + compile time evaluation. + 3) Code that triggers an OPcache bug. + +opcache.max_file_size (default "0") + Allows exclusion of large files from being cached. By default all files + are cached. + +opcache.consistency_checks (default "0") + Check the cache checksum each N requests. + The default value of "0" means that the checks are disabled. + Because calculating the checksum impairs performance, this directive should + be enabled only as part of a debugging process. + +opcache.force_restart_timeout (default "180") + How long to wait (in seconds) for a scheduled restart to begin if the cache + is not being accessed. + The OPcache uses this directive to identify a situation where there may + be a problem with a process. After this time period has passed, the + OPcache assumes that something has happened and starts killing the + processes that still hold the locks that are preventing a restart. + If the log level is 3 or above, a "killed locker" error is recorded + in the Apache logs when this happens. + +opcache.error_log + OPcache error_log file name. Empty string assumes "stderr". + +opcache.log_verbosity_level (default "1") + All OPcache errors go to the Web server log. + By default, only fatal errors (level 0) or errors (level 1) are logged. + You can also enable warnings (level 2), info messages (level 3) or + debug messages (level 4). + +opcache.preferred_memory_model + Preferred Shared Memory back-end. Leave empty and let the system decide. + +opcache.protect_memory (default "0") + Protect the shared memory from unexpected writing during script execution. + Useful for internal debugging only. + +opcache.mmap_base + Mapping base of shared memory segments (for Windows only). All the PHP + processes have to map shared memory into the same address space. This + directive allows to manually fix the "Unable to reattach to base address" + errors. diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c new file mode 100644 index 000000000..64c5aa8f1 --- /dev/null +++ b/ext/opcache/ZendAccelerator.c @@ -0,0 +1,2656 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "main/php.h" +#include "main/php_globals.h" +#include "zend.h" +#include "zend_extensions.h" +#include "zend_compile.h" +#include "ZendAccelerator.h" +#include "zend_persist.h" +#include "zend_shared_alloc.h" +#include "zend_accelerator_module.h" +#include "zend_accelerator_blacklist.h" +#include "zend_list.h" +#include "zend_execute.h" +#include "main/SAPI.h" +#include "main/php_streams.h" +#include "main/php_open_temporary_file.h" +#include "zend_API.h" +#include "zend_ini.h" +#include "TSRM/tsrm_virtual_cwd.h" +#include "zend_accelerator_util_funcs.h" +#include "zend_accelerator_hash.h" + +#ifndef ZEND_WIN32 +#include <netdb.h> +#endif + +#ifdef ZEND_WIN32 +typedef int uid_t; +typedef int gid_t; +#include <io.h> +#endif + +#ifndef ZEND_WIN32 +# include <sys/time.h> +#else +# include <process.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#include <fcntl.h> +#include <signal.h> +#include <time.h> + +#ifndef ZEND_WIN32 +# include <sys/types.h> +# include <sys/ipc.h> +#endif + +#include <sys/stat.h> +#include <errno.h> + +#define SHM_PROTECT() \ + do { \ + if (ZCG(accel_directives).protect_memory) { \ + zend_accel_shared_protect(1 TSRMLS_CC); \ + } \ + } while (0) +#define SHM_UNPROTECT() \ + do { \ + if (ZCG(accel_directives).protect_memory) { \ + zend_accel_shared_protect(0 TSRMLS_CC); \ + } \ + } while (0) + +ZEND_EXTENSION(); + +#ifndef ZTS +zend_accel_globals accel_globals; +#else +int accel_globals_id; +#endif + +/* Points to the structure shared across all PHP processes */ +zend_accel_shared_globals *accel_shared_globals = NULL; + +/* true globals, no need for thread safety */ +zend_bool accel_startup_ok = 0; +static char *zps_failure_reason = NULL; +char *zps_api_failure_reason = NULL; + +static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC); +static int (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle TSRMLS_DC); +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO +static char *(*accelerator_orig_zend_resolve_path)(const char *filename, int filename_len TSRMLS_DC); +#endif +static void (*orig_chdir)(INTERNAL_FUNCTION_PARAMETERS) = NULL; +static ZEND_INI_MH((*orig_include_path_on_modify)) = NULL; + +#ifdef ZEND_WIN32 +# define INCREMENT(v) InterlockedIncrement(&ZCSG(v)) +# define DECREMENT(v) InterlockedDecrement(&ZCSG(v)) +# define LOCKVAL(v) (ZCSG(v)) +#endif + +#ifdef ZEND_WIN32 +static time_t zend_accel_get_time(void) +{ + FILETIME now; + GetSystemTimeAsFileTime(&now); + + return (time_t) ((((((__int64)now.dwHighDateTime) << 32)|now.dwLowDateTime) - 116444736000000000L)/10000000); +} +#else +# define zend_accel_get_time() time(NULL) +#endif + +static inline int is_stream_path(const char *filename) +{ + const char *p; + + for (p = filename; isalnum((int)*p) || *p == '+' || *p == '-' || *p == '.'; p++); + return ((*p == ':') && (p - filename > 1) && (p[1] == '/') && (p[2] == '/')); +} + +/* O+ overrides PHP chdir() function and remembers the current working directory + * in ZCG(cwd) and ZCG(cwd_len). Later accel_getcwd() can use stored value and + * avoid getcwd() call. + */ +static ZEND_FUNCTION(accel_chdir) +{ + char cwd[MAXPATHLEN]; + + orig_chdir(INTERNAL_FUNCTION_PARAM_PASSTHRU); + if (VCWD_GETCWD(cwd, MAXPATHLEN)) { + if (ZCG(cwd)) { + efree(ZCG(cwd)); + } + ZCG(cwd_len) = strlen(cwd); + ZCG(cwd) = estrndup(cwd, ZCG(cwd_len)); + } else { + if (ZCG(cwd)) { + efree(ZCG(cwd)); + ZCG(cwd) = NULL; + } + } +} + +static inline char* accel_getcwd(int *cwd_len TSRMLS_DC) +{ + if (ZCG(cwd)) { + *cwd_len = ZCG(cwd_len); + return ZCG(cwd); + } else { + char cwd[MAXPATHLEN + 1]; + + if (!VCWD_GETCWD(cwd, MAXPATHLEN)) { + return NULL; + } + *cwd_len = ZCG(cwd_len) = strlen(cwd); + ZCG(cwd) = estrndup(cwd, ZCG(cwd_len)); + return ZCG(cwd); + } +} + +/* O+ tracks changes of "include_path" directive. It stores all the requested + * values in ZCG(include_paths) shared hash table, current value in + * ZCG(include_path)/ZCG(include_path_len) and one letter "path key" in + * ZCG(include_path_key). + */ +static ZEND_INI_MH(accel_include_path_on_modify) +{ + int ret = orig_include_path_on_modify(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + + ZCG(include_path_key) = NULL; + if (ret == SUCCESS) { + ZCG(include_path) = new_value; + if (ZCG(include_path) && *ZCG(include_path)) { + ZCG(include_path_len) = new_value_length; + + if (ZCG(enabled) && accel_startup_ok && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !zend_accel_hash_is_full(&ZCSG(include_paths))) { + + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + + ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1); + if (!ZCG(include_path_key) && + !zend_accel_hash_is_full(&ZCSG(include_paths))) { + char *key; + + key = zend_shared_alloc(ZCG(include_path_len) + 2); + if (key) { + memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1); + key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries; + ZCG(include_path_key) = key + ZCG(include_path_len) + 1; + zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key)); + } + } + + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } else { + ZCG(include_path_check) = 1; + } + } else { + ZCG(include_path) = ""; + ZCG(include_path_len) = 0; + } + } + return ret; +} + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +/* Interned strings support */ +static char *orig_interned_strings_start; +static char *orig_interned_strings_end; +static const char *(*orig_new_interned_string)(const char *str, int len, int free_src TSRMLS_DC); +static void (*orig_interned_strings_snapshot)(TSRMLS_D); +static void (*orig_interned_strings_restore)(TSRMLS_D); + +/* O+ disables creation of interned strings by regular PHP compiler, instead, + * it creates interned strings in shared memory when saves a script. + * Such interned strings are shared across all PHP processes + */ +static const char *accel_new_interned_string_for_php(const char *str, int len, int free_src TSRMLS_DC) +{ + return str; +} + +static void accel_interned_strings_snapshot_for_php(TSRMLS_D) +{ +} + +static void accel_interned_strings_restore_for_php(TSRMLS_D) +{ +} + +#ifndef ZTS +static void accel_interned_strings_restore_state(TSRMLS_D) +{ + unsigned int i; + + for (i = 0; i < ZCSG(interned_strings).nTableSize; i++) { + ZCSG(interned_strings).arBuckets[i] = ZCSG(interned_strings_saved_state).arBuckets[i]; + if (ZCSG(interned_strings).arBuckets[i]) { + ZCSG(interned_strings).arBuckets[i]->pLast = NULL; + } + } + ZCSG(interned_strings).pListHead = ZCSG(interned_strings_saved_state).pListHead; + ZCSG(interned_strings).pListTail = ZCSG(interned_strings_saved_state).pListTail; + if (ZCSG(interned_strings).pListHead) { + ZCSG(interned_strings).pListHead->pListLast = NULL; + } + if (ZCSG(interned_strings).pListTail) { + ZCSG(interned_strings).pListTail->pListNext = NULL; + } + ZCSG(interned_strings_top) = ZCSG(interned_strings_saved_state).top; +} + +static void accel_interned_strings_save_state(TSRMLS_D) +{ + ZCSG(interned_strings_saved_state).arBuckets = (Bucket**)zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(Bucket *)); + if (!ZCSG(interned_strings_saved_state).arBuckets) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + } + memcpy(ZCSG(interned_strings_saved_state).arBuckets, ZCSG(interned_strings).arBuckets, ZCSG(interned_strings).nTableSize * sizeof(Bucket *)); + ZCSG(interned_strings_saved_state).pListHead = ZCSG(interned_strings).pListHead; + ZCSG(interned_strings_saved_state).pListTail = ZCSG(interned_strings).pListTail; + ZCSG(interned_strings_saved_state).top = ZCSG(interned_strings_top); +} +#endif + +const char *accel_new_interned_string(const char *arKey, int nKeyLength, int free_src TSRMLS_DC) +{ +/* for now interned strings are supported only for non-ZTS build */ +#ifndef ZTS + ulong h; + uint nIndex; + Bucket *p; + + if (arKey >= ZCSG(interned_strings_start) && arKey < ZCSG(interned_strings_end)) { + /* this is already an interned string */ + return arKey; + } + + h = zend_inline_hash_func(arKey, nKeyLength); + nIndex = h & ZCSG(interned_strings).nTableMask; + + /* check for existing interned string */ + p = ZCSG(interned_strings).arBuckets[nIndex]; + while (p != NULL) { + if ((p->h == h) && (p->nKeyLength == (uint)nKeyLength)) { + if (!memcmp(p->arKey, arKey, nKeyLength)) { + if (free_src) { + efree((char*)arKey); + } + return p->arKey; + } + } + p = p->pNext; + } + + if (ZCSG(interned_strings_top) + ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength) >= + ZCSG(interned_strings_end)) { + /* no memory, return the same non-interned string */ + return arKey; + } + + /* create new interning string in shared interned strings buffer */ + p = (Bucket *) ZCSG(interned_strings_top); + ZCSG(interned_strings_top) += ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength); + + p->arKey = (char*)(p + 1); + memcpy((char*)p->arKey, arKey, nKeyLength); + p->nKeyLength = nKeyLength; + p->h = h; + p->pData = &p->pDataPtr; + p->pDataPtr = p; + + p->pNext = ZCSG(interned_strings).arBuckets[nIndex]; + p->pLast = NULL; + if (p->pNext) { + p->pNext->pLast = p; + } + ZCSG(interned_strings).arBuckets[nIndex] = p; + + p->pListLast = ZCSG(interned_strings).pListTail; + ZCSG(interned_strings).pListTail = p; + p->pListNext = NULL; + if (p->pListLast != NULL) { + p->pListLast->pListNext = p; + } + if (!ZCSG(interned_strings).pListHead) { + ZCSG(interned_strings).pListHead = p; + } + + ZCSG(interned_strings).nNumOfElements++; + + if (free_src) { + efree((char*)arKey); + } + + return p->arKey; +#else + return arKey; +#endif +} + +#ifndef ZTS +/* Copy PHP interned strings from PHP process memory into the shared memory */ +static void accel_use_shm_interned_strings(TSRMLS_D) +{ + Bucket *p, *q; + + /* function table hash keys */ + p = CG(function_table)->pListHead; + while (p) { + if (p->nKeyLength) { + p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + } + p = p->pListNext; + } + + /* class table hash keys, class names, properties, methods, constants, etc */ + p = CG(class_table)->pListHead; + while (p) { + zend_class_entry *ce = (zend_class_entry*)(p->pDataPtr); + + if (p->nKeyLength) { + p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + } + + if (ce->name) { + ce->name = accel_new_interned_string(ce->name, ce->name_length + 1, 0 TSRMLS_CC); + } + + q = ce->properties_info.pListHead; + while (q) { + zend_property_info *info = (zend_property_info*)(q->pData); + + if (q->nKeyLength) { + q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC); + } + + if (info->name) { + info->name = accel_new_interned_string(info->name, info->name_length + 1, 0 TSRMLS_CC); + } + + q = q->pListNext; + } + + q = ce->function_table.pListHead; + while (q) { + if (q->nKeyLength) { + q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC); + } + q = q->pListNext; + } + + q = ce->constants_table.pListHead; + while (q) { + if (q->nKeyLength) { + q->arKey = accel_new_interned_string(q->arKey, q->nKeyLength, 0 TSRMLS_CC); + } + q = q->pListNext; + } + + p = p->pListNext; + } + + /* constant hash keys */ + p = EG(zend_constants)->pListHead; + while (p) { + if (p->nKeyLength) { + p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + } + p = p->pListNext; + } + + /* auto globals hash keys and names */ + p = CG(auto_globals)->pListHead; + while (p) { + zend_auto_global *auto_global = (zend_auto_global*)p->pData; + + auto_global->name = accel_new_interned_string(auto_global->name, auto_global->name_len + 1, 0 TSRMLS_CC); + if (p->nKeyLength) { + p->arKey = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + } + p = p->pListNext; + } +} +#endif +#endif + +static inline void accel_restart_enter(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + INCREMENT(restart_in); +#else + static const FLOCK_STRUCTURE(restart_in_progress, F_WRLCK, SEEK_SET, 2, 1); + + if (fcntl(lock_file, F_SETLK, &restart_in_progress) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(+1): %s (%d)", strerror(errno), errno); + } +#endif + ZCSG(restart_in_progress) = 1; +} + +static inline void accel_restart_leave(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + ZCSG(restart_in_progress) = 0; + DECREMENT(restart_in); +#else + static const FLOCK_STRUCTURE(restart_finished, F_UNLCK, SEEK_SET, 2, 1); + + ZCSG(restart_in_progress) = 0; + if (fcntl(lock_file, F_SETLK, &restart_finished) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(-1): %s (%d)", strerror(errno), errno); + } +#endif +} + +static inline int accel_restart_is_active(TSRMLS_D) +{ + if (ZCSG(restart_in_progress)) { +#ifndef ZEND_WIN32 + FLOCK_STRUCTURE(restart_check, F_WRLCK, SEEK_SET, 2, 1); + + if (fcntl(lock_file, F_GETLK, &restart_check) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "RestartC: %s (%d)", strerror(errno), errno); + return FAILURE; + } + if (restart_check.l_type == F_UNLCK) { + ZCSG(restart_in_progress) = 0; + return 0; + } else { + return 1; + } +#else + return LOCKVAL(restart_in) != 0; +#endif + } + return 0; +} + +/* Creates a read lock for SHM access */ +static inline void accel_activate_add(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + INCREMENT(mem_usage); +#else + static const FLOCK_STRUCTURE(mem_usage_lock, F_RDLCK, SEEK_SET, 1, 1); + + if (fcntl(lock_file, F_SETLK, &mem_usage_lock) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(+1): %s (%d)", strerror(errno), errno); + } +#endif +} + +/* Releases a lock for SHM access */ +static inline void accel_deactivate_sub(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + if (ZCG(counted)) { + DECREMENT(mem_usage); + ZCG(counted) = 0; + } +#else + static const FLOCK_STRUCTURE(mem_usage_unlock, F_UNLCK, SEEK_SET, 1, 1); + + if (fcntl(lock_file, F_SETLK, &mem_usage_unlock) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(-1): %s (%d)", strerror(errno), errno); + } +#endif +} + +static inline void accel_unlock_all(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + accel_deactivate_sub(TSRMLS_C); +#else + static const FLOCK_STRUCTURE(mem_usage_unlock_all, F_UNLCK, SEEK_SET, 0, 0); + + if (fcntl(lock_file, F_SETLK, &mem_usage_unlock_all) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "UnlockAll: %s (%d)", strerror(errno), errno); + } +#endif +} + +#ifndef ZEND_WIN32 +static inline void kill_all_lockers(struct flock *mem_usage_check) +{ + int tries = 10; + + /* so that other process won't try to force while we are busy cleaning up */ + ZCSG(force_restart_time) = 0; + while (mem_usage_check->l_pid > 0) { + while (tries--) { + zend_accel_error(ACCEL_LOG_INFO, "Killed locker %d", mem_usage_check->l_pid); + if (kill(mem_usage_check->l_pid, SIGKILL)) { + break; + } + /* give it a chance to die */ + usleep(20000); + if (kill(mem_usage_check->l_pid, 0)) { + /* can't kill it */ + break; + } + usleep(10000); + } + if (!tries) { + zend_accel_error(ACCEL_LOG_INFO, "Can't kill %d after 20 tries!", mem_usage_check->l_pid); + ZCSG(force_restart_time) = time(NULL); /* restore forced restart request */ + } + + mem_usage_check->l_type = F_WRLCK; + mem_usage_check->l_whence = SEEK_SET; + mem_usage_check->l_start = 1; + mem_usage_check->l_len = 1; + mem_usage_check->l_pid = -1; + if (fcntl(lock_file, F_GETLK, mem_usage_check) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "KLockers: %s (%d)", strerror(errno), errno); + break; + } + + if (mem_usage_check->l_type == F_UNLCK || mem_usage_check->l_pid <= 0) { + break; + } + } +} +#endif + +static inline int accel_is_inactive(TSRMLS_D) +{ +#ifdef ZEND_WIN32 + if (LOCKVAL(mem_usage) == 0) { + return SUCCESS; + } +#else + FLOCK_STRUCTURE(mem_usage_check, F_WRLCK, SEEK_SET, 1, 1); + + mem_usage_check.l_pid = -1; + if (fcntl(lock_file, F_GETLK, &mem_usage_check) == -1) { + zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC: %s (%d)", strerror(errno), errno); + return FAILURE; + } + if (mem_usage_check.l_type == F_UNLCK) { + return SUCCESS; + } + + if (ZCG(accel_directives).force_restart_timeout + && ZCSG(force_restart_time) + && time(NULL) >= ZCSG(force_restart_time)) { + zend_accel_error(ACCEL_LOG_WARNING, "Forced restart at %d (after %d seconds), locked by %d", time(NULL), ZCG(accel_directives).force_restart_timeout, mem_usage_check.l_pid); + kill_all_lockers(&mem_usage_check); + + return FAILURE; /* next request should be able to restart it */ + } +#endif + + return FAILURE; +} + +static int zend_get_stream_timestamp(const char *filename, struct stat *statbuf TSRMLS_DC) +{ + php_stream_wrapper *wrapper; + php_stream_statbuf stream_statbuf; + int ret, er; + + if (!filename) { + return FAILURE; + } + + wrapper = php_stream_locate_url_wrapper(filename, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC); + if (!wrapper) { + return FAILURE; + } + if (!wrapper->wops || !wrapper->wops->url_stat) { + statbuf->st_mtime = 1; + return SUCCESS; /* anything other than 0 is considered to be a valid timestamp */ + } + + er = EG(error_reporting); + EG(error_reporting) = 0; + zend_try { + ret = wrapper->wops->url_stat(wrapper, (char*)filename, PHP_STREAM_URL_STAT_QUIET, &stream_statbuf, NULL TSRMLS_CC); + } zend_catch { + ret = -1; + } zend_end_try(); + EG(error_reporting) = er; + + if (ret != 0) { + return FAILURE; + } + + *statbuf = stream_statbuf.sb; + return SUCCESS; +} + +#if ZEND_WIN32 +static accel_time_t zend_get_file_handle_timestamp_win(zend_file_handle *file_handle, size_t *size) +{ + static unsigned __int64 utc_base = 0; + static FILETIME utc_base_ft; + WIN32_FILE_ATTRIBUTE_DATA fdata; + + if (!file_handle->opened_path) { + return 0; + } + + if (!utc_base) { + SYSTEMTIME st; + + st.wYear = 1970; + st.wMonth = 1; + st.wDay = 1; + st.wHour = 0; + st.wMinute = 0; + st.wSecond = 0; + st.wMilliseconds = 0; + + SystemTimeToFileTime (&st, &utc_base_ft); + utc_base = (((unsigned __int64)utc_base_ft.dwHighDateTime) << 32) + utc_base_ft.dwLowDateTime; + } + + if (GetFileAttributesEx(file_handle->opened_path, GetFileExInfoStandard, &fdata) != 0) { + unsigned __int64 ftime; + + if (CompareFileTime (&fdata.ftLastWriteTime, &utc_base_ft) < 0) { + return 0; + } + + ftime = (((unsigned __int64)fdata.ftLastWriteTime.dwHighDateTime) << 32) + fdata.ftLastWriteTime.dwLowDateTime - utc_base; + ftime /= 10000000L; + + if (size) { + *size = (size_t)(((unsigned __int64)fdata.nFileSizeHigh) << 32 + (unsigned __int64)fdata.nFileSizeLow); + } + return (accel_time_t)ftime; + } + return 0; +} +#endif + +static accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size TSRMLS_DC) +{ + struct stat statbuf; +#ifdef ZEND_WIN32 + accel_time_t res; +#endif + + if (sapi_module.get_stat && + !EG(opline_ptr) && + file_handle->filename == SG(request_info).path_translated) { + + struct stat *tmpbuf = sapi_module.get_stat(TSRMLS_C); + + if (tmpbuf) { + if (size) { + *size = tmpbuf->st_size; + } + return tmpbuf->st_mtime; + } + } + +#ifdef ZEND_WIN32 + res = zend_get_file_handle_timestamp_win(file_handle, size); + if (res) { + return res; + } +#endif + + switch (file_handle->type) { + case ZEND_HANDLE_FD: + if (fstat(file_handle->handle.fd, &statbuf) == -1) { + return 0; + } + break; + case ZEND_HANDLE_FP: + if (fstat(fileno(file_handle->handle.fp), &statbuf) == -1) { + if (zend_get_stream_timestamp(file_handle->filename, &statbuf TSRMLS_CC) != SUCCESS) { + return 0; + } + } + break; + case ZEND_HANDLE_FILENAME: +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + case ZEND_HANDLE_MAPPED: +#endif + { + char *file_path = file_handle->opened_path; + + if (file_path) { + if (is_stream_path(file_path)) { + if (zend_get_stream_timestamp(file_path, &statbuf TSRMLS_CC) == SUCCESS) { + break; + } + } + if (VCWD_STAT(file_path, &statbuf) != -1) { + break; + } + } + + if (zend_get_stream_timestamp(file_handle->filename, &statbuf TSRMLS_CC) != SUCCESS) { + return 0; + } + break; + } + case ZEND_HANDLE_STREAM: + { + php_stream *stream = (php_stream *)file_handle->handle.stream.handle; + php_stream_statbuf sb; + int ret, er; + + if (!stream || + !stream->ops || + !stream->ops->stat) { + return 0; + } + + er = EG(error_reporting); + EG(error_reporting) = 0; + zend_try { + ret = stream->ops->stat(stream, &sb TSRMLS_CC); + } zend_catch { + ret = -1; + } zend_end_try(); + EG(error_reporting) = er; + if (ret != 0) { + return 0; + } + + statbuf = sb.sb; + } + break; + + default: + return 0; + } + + if (size) { + *size = statbuf.st_size; + } + return statbuf.st_mtime; +} + +static inline int do_validate_timestamps(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC) +{ + zend_file_handle ps_handle; + char actualpath [MAXPATHLEN + 1]; + char *full_path_ptr = NULL; + + /** check that the persistant script is indeed the same file we cached + * (if part of the path is a symlink than it possible that the user will change it) + * See bug #15140 + */ + if (file_handle->opened_path) { + if (strcmp(persistent_script->full_path, file_handle->opened_path) != 0) { + return FAILURE; + } + } else { + full_path_ptr = VCWD_REALPATH(file_handle->filename, actualpath); + if (full_path_ptr && strcmp(persistent_script->full_path, full_path_ptr) != 0) { + return FAILURE; + } + file_handle->opened_path = full_path_ptr; + } + + if (persistent_script->timestamp == 0) { + if (full_path_ptr) { + file_handle->opened_path = NULL; + } + return FAILURE; + } + + if (zend_get_file_handle_timestamp(file_handle, NULL TSRMLS_CC) == persistent_script->timestamp) { + if (full_path_ptr) { + file_handle->opened_path = NULL; + } + return SUCCESS; + } + if (full_path_ptr) { + file_handle->opened_path = NULL; + } + + ps_handle.type = ZEND_HANDLE_FILENAME; + ps_handle.filename = persistent_script->full_path; + ps_handle.opened_path = persistent_script->full_path; + + if (zend_get_file_handle_timestamp(&ps_handle, NULL TSRMLS_CC) == persistent_script->timestamp) { + return SUCCESS; + } + + return FAILURE; +} + +static void zend_accel_schedule_restart_if_necessary(TSRMLS_D) +{ + if ((((double) ZSMMG(wasted_shared_memory)) / ZCG(accel_directives).memory_consumption) >= ZCG(accel_directives).max_wasted_percentage) { + ZSMMG(memory_exhausted) = 1; + zend_accel_schedule_restart(ACCEL_RESTART_WASTED TSRMLS_CC); + } +} + +static inline int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle TSRMLS_DC) +{ + if (ZCG(accel_directives).revalidate_freq && + (persistent_script->dynamic_members.revalidate >= ZCSG(revalidate_at))) { + return SUCCESS; + } else if (do_validate_timestamps(persistent_script, file_handle TSRMLS_CC) == FAILURE) { + return FAILURE; + } else { + persistent_script->dynamic_members.revalidate = ZCSG(revalidate_at); + return SUCCESS; + } +} + +static unsigned int zend_accel_script_checksum(zend_persistent_script *persistent_script) +{ + signed char *mem = (signed char*)persistent_script->mem; + size_t size = persistent_script->size; + size_t persistent_script_check_block_size = ((char *)&(persistent_script->dynamic_members)) - (char *)persistent_script; + unsigned int checksum = ADLER32_INIT; + + if (mem < (signed char*)persistent_script) { + checksum = zend_adler32(checksum, mem, (signed char*)persistent_script - mem); + size -= (signed char*)persistent_script - mem; + mem += (signed char*)persistent_script - mem; + } + + zend_adler32(checksum, mem, persistent_script_check_block_size); + mem += sizeof(*persistent_script); + size -= sizeof(*persistent_script); + + if (size > 0) { + checksum = zend_adler32(checksum, mem, size); + } + return checksum; +} + +/* Instead of resolving full real path name each time we need to identify file, + * we create a key that consist from requested file name, current working + * directory, current include_path, etc */ +char *accel_make_persistent_key_ex(zend_file_handle *file_handle, int path_length, int *key_len TSRMLS_DC) +{ + int key_length; + + /* CWD and include_path don't matter for absolute file names and streams */ + if (ZCG(accel_directives).use_cwd && + !IS_ABSOLUTE_PATH(file_handle->filename, path_length) && + !is_stream_path(file_handle->filename)) { + char *include_path = NULL; + int include_path_len = 0; + const char *parent_script = NULL; + int parent_script_len = 0; + int cur_len = 0; + int cwd_len; + char *cwd; + + if ((cwd = accel_getcwd(&cwd_len TSRMLS_CC)) == NULL) { + /* we don't handle this well for now. */ + zend_accel_error(ACCEL_LOG_INFO, "getcwd() failed for '%s' (%d), please try to set opcache.use_cwd to 0 in ini file", file_handle->filename, errno); + if (file_handle->opened_path) { + cwd = file_handle->opened_path; + cwd_len = strlen(cwd); + } else { + ZCG(key_len) = 0; + return NULL; + } + } + + if (ZCG(include_path_key)) { + include_path = ZCG(include_path_key); + include_path_len = 1; + } else { + include_path = ZCG(include_path); + include_path_len = ZCG(include_path_len); + if (ZCG(include_path_check) && + ZCG(enabled) && accel_startup_ok && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !zend_accel_hash_is_full(&ZCSG(include_paths))) { + + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + + ZCG(include_path_key) = zend_accel_hash_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1); + if (ZCG(include_path_key)) { + include_path = ZCG(include_path_key); + include_path_len = 1; + } else if (!zend_accel_hash_is_full(&ZCSG(include_paths))) { + char *key; + + key = zend_shared_alloc(ZCG(include_path_len) + 2); + if (key) { + memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1); + key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries; + ZCG(include_path_key) = key + ZCG(include_path_len) + 1; + zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key)); + include_path = ZCG(include_path_key); + include_path_len = 1; + } + } + + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } + } + + /* Here we add to the key the parent script directory, + since fopen_wrappers from version 4.0.7 use current script's path + in include path too. + */ + if (EG(in_execution) && + (parent_script = zend_get_executed_filename(TSRMLS_C)) != NULL && + parent_script[0] != '[') { + + parent_script_len = strlen(parent_script); + while ((--parent_script_len > 0) && !IS_SLASH(parent_script[parent_script_len])); + } + + /* Calculate key length */ + key_length = cwd_len + path_length + include_path_len + 2; + if (parent_script_len) { + key_length += parent_script_len + 1; + } + + /* Generate key + * Note - the include_path must be the last element in the key, + * since in itself, it may include colons (which we use to separate + * different components of the key) + */ + if ((size_t)key_length >= sizeof(ZCG(key))) { + ZCG(key_len) = 0; + return NULL; + } + memcpy(ZCG(key), cwd, cwd_len); + ZCG(key)[cwd_len] = ':'; + + memcpy(ZCG(key) + cwd_len + 1, file_handle->filename, path_length); + + ZCG(key)[cwd_len + 1 + path_length] = ':'; + + cur_len = cwd_len + 1 + path_length + 1; + + if (parent_script_len) { + memcpy(ZCG(key) + cur_len, parent_script, parent_script_len); + cur_len += parent_script_len; + ZCG(key)[cur_len] = ':'; + cur_len++; + } + memcpy(ZCG(key) + cur_len, include_path, include_path_len); + ZCG(key)[key_length] = '\0'; + } else { + /* not use_cwd */ + key_length = path_length; + if ((size_t)key_length >= sizeof(ZCG(key))) { + ZCG(key_len) = 0; + return NULL; + } + memcpy(ZCG(key), file_handle->filename, key_length + 1); + } + + *key_len = ZCG(key_len) = key_length; + return ZCG(key); +} + +static inline char *accel_make_persistent_key(zend_file_handle *file_handle, int *key_len TSRMLS_DC) +{ + return accel_make_persistent_key_ex(file_handle, strlen(file_handle->filename), key_len TSRMLS_CC); +} + +/* Adds another key for existing cached script */ +static void zend_accel_add_key(char *key, unsigned int key_length, zend_accel_hash_entry *bucket TSRMLS_DC) +{ + if (!zend_accel_hash_find(&ZCSG(hash), key, key_length + 1)) { + if (zend_accel_hash_is_full(&ZCSG(hash))) { + zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!"); + ZSMMG(memory_exhausted) = 1; + zend_accel_schedule_restart(ACCEL_RESTART_HASH TSRMLS_CC); + } else { + char *new_key = zend_shared_alloc(key_length + 1); + if (new_key) { + memcpy(new_key, key, key_length + 1); + zend_accel_hash_update(&ZCSG(hash), new_key, key_length + 1, 1, bucket); + } + } + } +} + +static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length, int *from_shared_memory TSRMLS_DC) +{ + zend_accel_hash_entry *bucket; + uint memory_used; + + /* Check if script may be stored in shared memory */ + if (!zend_accel_script_persistable(new_persistent_script)) { + return new_persistent_script; + } + + /* exclusive lock */ + zend_shared_alloc_lock(TSRMLS_C); + + if (zend_accel_hash_is_full(&ZCSG(hash))) { + zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!"); + ZSMMG(memory_exhausted) = 1; + zend_accel_schedule_restart(ACCEL_RESTART_HASH TSRMLS_CC); + zend_shared_alloc_unlock(TSRMLS_C); + return new_persistent_script; + } + + /* Check if we still need to put the file into the cache (may be it was + * already stored by another process. This final check is done under + * exclusive lock) */ + bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->full_path, new_persistent_script->full_path_len + 1); + if (bucket) { + zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data; + + if (!existing_persistent_script->corrupted) { + if (!ZCG(accel_directives).validate_timestamps || + (new_persistent_script->timestamp == existing_persistent_script->timestamp)) { + zend_accel_add_key(key, key_length, bucket TSRMLS_CC); + } + zend_shared_alloc_unlock(TSRMLS_C); + return new_persistent_script; + } + } + + /* Calculate the required memory size */ + memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length TSRMLS_CC); + + /* Allocate shared memory */ + ZCG(mem) = zend_shared_alloc(memory_used); + if (!ZCG(mem)) { + zend_shared_alloc_unlock(TSRMLS_C); + return new_persistent_script; + } + + /* cleanup after calculation */ + new_persistent_script->mem = ZCG(mem); + new_persistent_script->size = memory_used; + + /* Copy into shared memory */ + new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length TSRMLS_CC); + + /* Consistency check */ + if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) { + zend_accel_error( + ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING, + "Internal error: wrong size calculation: %s start=0x%08x, end=0x%08x, real=0x%08x\n", + new_persistent_script->full_path, + new_persistent_script->mem, + (char *)new_persistent_script->mem + new_persistent_script->size, + ZCG(mem)); + } + + new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script); + + /* store script structure in the hash table */ + bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->full_path, new_persistent_script->full_path_len + 1, 0, new_persistent_script); + if (bucket && + (new_persistent_script->full_path_len != key_length || + memcmp(new_persistent_script->full_path, key, key_length) != 0)) { + /* link key to the same persistent script in hash table */ + if (!zend_accel_hash_update(&ZCSG(hash), key, key_length + 1, 1, bucket)) { + zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!"); + ZSMMG(memory_exhausted) = 1; + zend_accel_schedule_restart(ACCEL_RESTART_HASH TSRMLS_CC); + } + } + + new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size); + + zend_shared_alloc_unlock(TSRMLS_C); + + *from_shared_memory = 1; + return new_persistent_script; +} + +static const struct jit_auto_global_info +{ + const char *name; + size_t len; +} jit_auto_globals_info[] = { + { "_SERVER", sizeof("_SERVER")}, + { "_ENV", sizeof("_ENV")}, + { "_REQUEST", sizeof("_REQUEST")}, +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + { "GLOBALS", sizeof("GLOBALS")}, +#endif +}; + +static int zend_accel_get_auto_globals(TSRMLS_D) +{ + int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0])); + int n = 1; + int mask = 0; + + for (i = 0; i < ag_size ; i++) { + if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_info[i].name, jit_auto_globals_info[i].len)) { + mask |= n; + } + n += n; + } + return mask; +} + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +static int zend_accel_get_auto_globals_no_jit(TSRMLS_D) +{ + if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_info[3].name, jit_auto_globals_info[3].len)) { + return 8; + } + return 0; +} +#endif + +static void zend_accel_set_auto_globals(int mask TSRMLS_DC) +{ + int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0])); + int n = 1; + + for (i = 0; i < ag_size ; i++) { + if (mask & n) { + zend_is_auto_global(jit_auto_globals_info[i].name, jit_auto_globals_info[i].len - 1 TSRMLS_CC); + } + n += n; + } +} + +static zend_persistent_script *compile_and_cache_file(zend_file_handle *file_handle, int type, char *key, unsigned int key_length, zend_op_array **op_array_p, int *from_shared_memory TSRMLS_DC) +{ + zend_persistent_script *new_persistent_script; + zend_op_array *orig_active_op_array; + HashTable *orig_function_table, *orig_class_table; + zval *orig_user_error_handler; + zend_op_array *op_array; + int do_bailout = 0; + accel_time_t timestamp = 0; +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + zend_uint orig_compiler_options = 0; +#endif + + /* Try to open file */ + if (file_handle->type == ZEND_HANDLE_FILENAME) { + if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle TSRMLS_CC) == SUCCESS) { + /* key may be changed by zend_stream_open_function() */ + if (key == ZCG(key)) { + key_length = ZCG(key_len); + } + } else { + *op_array_p = NULL; + if (type == ZEND_REQUIRE) { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC); + zend_bailout(); + } else { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC); + } + return NULL; + } + } + + /* check blacklist right after ensuring that file was opened */ + if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, file_handle->opened_path)) { + ZCSG(blacklist_misses)++; + *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + return NULL; + } + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if (file_handle->type == ZEND_HANDLE_STREAM && + (!strstr(file_handle->filename, ".phar") || + strstr(file_handle->filename, "://"))) { + char *buf; + size_t size; + + /* Stream callbacks needs to be called in context of original + * function and class tables (see: https://bugs.php.net/bug.php?id=64353) + */ + if (zend_stream_fixup(file_handle, &buf, &size TSRMLS_CC) == FAILURE) { + *op_array_p = NULL; + return NULL; + } + } +#endif + + if (ZCG(accel_directives).validate_timestamps || ZCG(accel_directives).max_file_size > 0) { + size_t size = 0; + + /* Obtain the file timestamps, *before* actually compiling them, + * otherwise we have a race-condition. + */ + timestamp = zend_get_file_handle_timestamp(file_handle, ZCG(accel_directives).max_file_size > 0 ? &size : NULL TSRMLS_CC); + + /* If we can't obtain a timestamp (that means file is possibly socket) + * we won't cache it + */ + if (timestamp == 0) { + *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + return NULL; + } + + if (ZCG(accel_directives).max_file_size > 0 && size > (size_t)ZCG(accel_directives).max_file_size) { + ZCSG(blacklist_misses)++; + *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + return NULL; + } + } + + new_persistent_script = create_persistent_script(); + + /* Save the original values for the op_array, function table and class table */ + orig_active_op_array = CG(active_op_array); + orig_function_table = CG(function_table); + orig_class_table = CG(class_table); + orig_user_error_handler = EG(user_error_handler); + + /* Override them with ours */ + CG(function_table) = &ZCG(function_table); + EG(class_table) = CG(class_table) = &new_persistent_script->class_table; + EG(user_error_handler) = NULL; + + zend_try { +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + orig_compiler_options = CG(compiler_options); + CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY; + CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES; + CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING; + CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION; +#endif + op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type TSRMLS_CC); +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + CG(compiler_options) = orig_compiler_options; +#endif + } zend_catch { + op_array = NULL; + do_bailout = 1; +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + CG(compiler_options) = orig_compiler_options; +#endif + } zend_end_try(); + + /* Restore originals */ + CG(active_op_array) = orig_active_op_array; + CG(function_table) = orig_function_table; + EG(class_table) = CG(class_table) = orig_class_table; + EG(user_error_handler) = orig_user_error_handler; + + if (!op_array) { + /* compilation failed */ + free_persistent_script(new_persistent_script, 1); + zend_accel_free_user_functions(&ZCG(function_table) TSRMLS_CC); + if (do_bailout) { + zend_bailout(); + } + return NULL; + } + + /* Build the persistent_script structure. + Here we aren't sure we would store it, but we will need it + further anyway. + */ + zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->function_table TSRMLS_CC); + new_persistent_script->main_op_array = *op_array; + + efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */ + + /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we + will have to ping the used auto global variables before execution */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (PG(auto_globals_jit)) { + new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals(TSRMLS_C); + } else { + new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit(TSRMLS_C); + } +#else + if ((PG(auto_globals_jit) && !PG(register_globals) && !PG(register_long_arrays))) { + new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals(TSRMLS_C); + } +#endif + + if (ZCG(accel_directives).validate_timestamps) { + /* Obtain the file timestamps, *before* actually compiling them, + * otherwise we have a race-condition. + */ + new_persistent_script->timestamp = timestamp; + new_persistent_script->dynamic_members.revalidate = ZCSG(revalidate_at); + } + + if (file_handle->opened_path) { + new_persistent_script->full_path_len = strlen(file_handle->opened_path); + new_persistent_script->full_path = estrndup(file_handle->opened_path, new_persistent_script->full_path_len); + } else { + new_persistent_script->full_path_len = strlen(file_handle->filename); + new_persistent_script->full_path = estrndup(file_handle->filename, new_persistent_script->full_path_len); + } + new_persistent_script->hash_value = zend_hash_func(new_persistent_script->full_path, new_persistent_script->full_path_len + 1); + + /* Now persistent_script structure is ready in process memory */ + return cache_script_in_shared_memory(new_persistent_script, key, key_length, from_shared_memory TSRMLS_CC); +} + +/* zend_compile() replacement */ +static zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) +{ + zend_persistent_script *persistent_script = NULL; + char *key = NULL; + int key_length; + int from_shared_memory; /* if the script we've got is stored in SHM */ + + if (!file_handle->filename || + !ZCG(enabled) || !accel_startup_ok || + (!ZCG(counted) && !ZCSG(accelerator_enabled)) || + CG(interactive) || + (ZCSG(restart_in_progress) && accel_restart_is_active(TSRMLS_C))) { + /* The Accelerator is disabled, act as if without the Accelerator */ + return accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + } + + /* Make sure we only increase the currently running processes semaphore + * once each execution (this function can be called more than once on + * each execution) + */ + if (!ZCG(counted)) { + ZCG(counted) = 1; + accel_activate_add(TSRMLS_C); + } + + /* In case this callback is called from include_once, require_once or it's + * a main FastCGI request, the key must be already calculated, and cached + * persistent script already found */ + if ((EG(opline_ptr) == NULL && + ZCG(cache_opline) == NULL && + file_handle->filename == SG(request_info).path_translated && + ZCG(cache_persistent_script)) || + (EG(opline_ptr) && *EG(opline_ptr) && + *EG(opline_ptr) == ZCG(cache_opline) && + (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL && +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) { +#else + ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) { +#endif + if (!ZCG(key_len)) { + return accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + } + /* persistent script was already found by overridden open() or + * resolve_path() callbacks */ + persistent_script = ZCG(cache_persistent_script); + key = ZCG(key); + key_length = ZCG(key_len); + } else { + /* try to find cached script by key */ + if ((key = accel_make_persistent_key(file_handle, &key_length TSRMLS_CC)) == NULL) { + return accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + } + persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length + 1); + if (!persistent_script) { + /* try to find cached script by full real path */ + zend_accel_hash_entry *bucket; + + /* open file to resolve the path */ + if (file_handle->type == ZEND_HANDLE_FILENAME && +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) { +#else + zend_stream_open(file_handle->filename, file_handle TSRMLS_CC) == FAILURE) { +#endif + if (type == ZEND_REQUIRE) { + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC); + zend_bailout(); + } else { + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC); + } + return NULL; + } + + if (file_handle->opened_path && + (bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path, strlen(file_handle->opened_path) + 1)) != NULL) { + + persistent_script = (zend_persistent_script *)bucket->data; + if (!ZCG(accel_directives).revalidate_path && + !persistent_script->corrupted) { + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + zend_accel_add_key(key, key_length, bucket TSRMLS_CC); + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } + } + } + } + + /* clear cache */ + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + + if (persistent_script && persistent_script->corrupted) { + persistent_script = NULL; + } + + SHM_UNPROTECT(); + + /* If script is found then validate_timestamps if option is enabled */ + if (persistent_script && ZCG(accel_directives).validate_timestamps) { + if (validate_timestamp_and_record(persistent_script, file_handle TSRMLS_CC) == FAILURE) { + zend_shared_alloc_lock(TSRMLS_C); + if (!persistent_script->corrupted) { + persistent_script->corrupted = 1; + persistent_script->timestamp = 0; + ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption; + zend_accel_schedule_restart_if_necessary(TSRMLS_C); + } + zend_shared_alloc_unlock(TSRMLS_C); + persistent_script = NULL; + } + } + + /* if turned on - check the compiled script ADLER32 checksum */ + if (persistent_script && ZCG(accel_directives).consistency_checks + && persistent_script->dynamic_members.hits % ZCG(accel_directives).consistency_checks == 0) { + + unsigned int checksum = zend_accel_script_checksum(persistent_script); + if (checksum != persistent_script->dynamic_members.checksum ) { + /* The checksum is wrong */ + zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s': expected=0x%0.8X, found=0x%0.8X", + persistent_script->full_path, persistent_script->dynamic_members.checksum, checksum); + zend_shared_alloc_lock(TSRMLS_C); + if (!persistent_script->corrupted) { + persistent_script->corrupted = 1; + persistent_script->timestamp = 0; + ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption; + zend_accel_schedule_restart_if_necessary(TSRMLS_C); + } + zend_shared_alloc_unlock(TSRMLS_C); + persistent_script = NULL; + } + } + + /* If script was not found or invalidated by validate_timestamps */ + if (!persistent_script) { + zend_op_array *op_array; + + /* Cache miss.. */ + ZCSG(misses)++; + + /* No memory left. Behave like without the Accelerator */ + if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) { + SHM_PROTECT(); + return accelerator_orig_compile_file(file_handle, type TSRMLS_CC); + } + + /* Try and cache the script and assume that it is returned from_shared_memory. + * If it isn't compile_and_cache_file() changes the flag to 0 + */ + from_shared_memory = 0; + persistent_script = compile_and_cache_file(file_handle, type, key, key_length, &op_array, &from_shared_memory TSRMLS_CC); + + /* Something went wrong during compilation, returning NULL */ + if (!persistent_script) { + SHM_PROTECT(); + return op_array; /* Presently always NULL, but not necessary in the future */ + } + } else { + +#if !ZEND_WIN32 + ZCSG(hits)++; /* TBFixed: may lose one hit */ + persistent_script->dynamic_members.hits++; /* see above */ +#else + InterlockedIncrement(&ZCSG(hits)); + InterlockedIncrement(&persistent_script->dynamic_members.hits); +#endif + + /* see bug #15471 (old BTS) */ + if (persistent_script->full_path) { + if (!EG(opline_ptr) || !*EG(opline_ptr) || + (*EG(opline_ptr))->opcode != ZEND_INCLUDE_OR_EVAL || +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ((*EG(opline_ptr))->extended_value != ZEND_INCLUDE_ONCE && + (*EG(opline_ptr))->extended_value != ZEND_REQUIRE_ONCE)) { +#else + ((*EG(opline_ptr))->op2.u.constant.value.lval != ZEND_INCLUDE_ONCE && + (*EG(opline_ptr))->op2.u.constant.value.lval != ZEND_REQUIRE_ONCE)) { +#endif + void *dummy = (void *) 1; + + zend_hash_quick_add(&EG(included_files), persistent_script->full_path, persistent_script->full_path_len + 1, persistent_script->hash_value, &dummy, sizeof(void *), NULL); + } + } + zend_file_handle_dtor(file_handle TSRMLS_CC); + from_shared_memory = 1; + } + + persistent_script->dynamic_members.last_used = ZCG(request_time); + + SHM_PROTECT(); + + /* Fetch jit auto globals used in the script before execution */ + if (persistent_script->ping_auto_globals_mask) { + zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask TSRMLS_CC); + } + + return zend_accel_load_script(persistent_script, from_shared_memory TSRMLS_CC); +} + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + +/* Taken from php-5.2.5 because early versions don't have correct version */ +static char *accel_tsrm_realpath(const char *path, int path_len TSRMLS_DC) +{ + cwd_state new_state; + char *real_path; + char *cwd; + int cwd_len; + + /* realpath("") returns CWD */ + if (!*path) { + new_state.cwd = (char*)malloc(1); + if (!new_state.cwd) { + zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed"); + return NULL; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + if ((cwd = accel_getcwd(&cwd_len TSRMLS_CC)) != NULL) { + path = cwd; + } + } else if (!IS_ABSOLUTE_PATH(path, path_len) && + (cwd = accel_getcwd(&cwd_len TSRMLS_CC)) != NULL) { + new_state.cwd = zend_strndup(cwd, cwd_len); + if (!new_state.cwd) { + zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed"); + return NULL; + } + new_state.cwd_length = cwd_len; + } else { + new_state.cwd = (char*)malloc(1); + if (!new_state.cwd) { + zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed"); + return NULL; + } + new_state.cwd[0] = '\0'; + new_state.cwd_length = 0; + } + +#ifndef CWD_REALPATH +# define CWD_REALPATH 2 +#endif + if (virtual_file_ex(&new_state, path, NULL, CWD_REALPATH)) { + free(new_state.cwd); + return NULL; + } + + real_path = emalloc(new_state.cwd_length + 1); + memcpy(real_path, new_state.cwd, new_state.cwd_length + 1); + free(new_state.cwd); + return real_path; +} + +static char *accel_php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC) +{ + char *resolved_path; + char trypath[MAXPATHLEN]; + const char *ptr, *end; + int len; + + if (!filename) { + return NULL; + } + + if (*filename == '.' || + IS_ABSOLUTE_PATH(filename, filename_length) || + !path || + !*path) { + return accel_tsrm_realpath(filename, filename_length TSRMLS_CC); + } + + ptr = path; + while (*ptr) { + for (end = ptr; *end && *end != DEFAULT_DIR_SEPARATOR; end++); + len = end - ptr; + if (*end) end++; + if (len + 1 + filename_length + 1 >= MAXPATHLEN) { + ptr = end; + continue; + } + memcpy(trypath, ptr, len); + trypath[len] = '/'; + memcpy(trypath + len + 1, filename, filename_length + 1); + ptr = end; + if ((resolved_path = accel_tsrm_realpath(trypath, len + 1 + filename_length TSRMLS_CC)) != NULL) { + return resolved_path; + } + } /* end provided path */ + + /* check in calling scripts' current working directory as a fall back case + */ + if (EG(in_execution)) { + char *exec_fname = zend_get_executed_filename(TSRMLS_C); + int exec_fname_length = strlen(exec_fname); + + while ((--exec_fname_length >= 0) && !IS_SLASH(exec_fname[exec_fname_length])); + if (exec_fname && exec_fname[0] != '[' && + exec_fname_length > 0 && + exec_fname_length + 1 + filename_length + 1 < MAXPATHLEN) { + memcpy(trypath, exec_fname, exec_fname_length + 1); + memcpy(trypath + exec_fname_length + 1, filename, filename_length + 1); + return accel_tsrm_realpath(trypath, exec_fname_length + 1 + filename_length TSRMLS_CC); + } + } + + return NULL; +} + +/* zend_stream_open_function() replacement for PHP 5.2 */ +static int persistent_stream_open_function(const char *filename, zend_file_handle *handle TSRMLS_DC) +{ + if (ZCG(enabled) && accel_startup_ok && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !CG(interactive) && + !ZCSG(restart_in_progress)) { + + if (EG(opline_ptr) && *EG(opline_ptr)) { + zend_op *opline = *EG(opline_ptr); + + if (opline->opcode == ZEND_INCLUDE_OR_EVAL && + (opline->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE || + opline->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE)) { + /* we are in include_once */ + char *key = NULL; + int key_length; + char *resolved_path; + zend_accel_hash_entry *bucket; + zend_persistent_script *persistent_script; + int filename_len; + + if (opline->op1.op_type == IS_CONST) { + filename_len = Z_STRLEN(opline->op1.u.constant); + } else { + filename_len = strlen(filename); + } + handle->filename = (char*)filename; + handle->free_filename = 0; + handle->opened_path = NULL; + + /* Check if requested file already cached (by full name) */ + if (IS_ABSOLUTE_PATH(filename, filename_len) && + (persistent_script = zend_accel_hash_find(&ZCSG(hash), (char*)filename, filename_len + 1)) != NULL && + !persistent_script->corrupted) { + + handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len); + handle->type = ZEND_HANDLE_FILENAME; + memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len + 1); + ZCG(key_len) = persistent_script->full_path_len; + ZCG(cache_opline) = opline; + ZCG(cache_persistent_script) = persistent_script; + return SUCCESS; + } + + /* Check if requested file already cached (by key) */ + key = accel_make_persistent_key_ex(handle, filename_len, &key_length TSRMLS_CC); + if (!ZCG(accel_directives).revalidate_path && + key && + (persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length + 1)) != NULL && + !persistent_script->corrupted) { + + handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len); + handle->type = ZEND_HANDLE_FILENAME; + ZCG(cache_opline) = opline; + ZCG(cache_persistent_script) = persistent_script; + return SUCCESS; + } + + /* find the full real path */ + resolved_path = accel_php_resolve_path(filename, filename_len, ZCG(include_path) TSRMLS_CC); + + /* Check if requested file already cached (by real name) */ + if (resolved_path && + (bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path, strlen(resolved_path) + 1)) != NULL) { + + persistent_script = (zend_persistent_script *)bucket->data; + if (persistent_script && !persistent_script->corrupted) { + handle->opened_path = resolved_path; + handle->type = ZEND_HANDLE_FILENAME; + if (key && !ZCG(accel_directives).revalidate_path) { + /* add another "key" for the same bucket */ + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + zend_accel_add_key(key, key_length, bucket TSRMLS_CC); + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } + ZCG(cache_opline) = opline; + ZCG(cache_persistent_script) = persistent_script; + return SUCCESS; + } + } + if (resolved_path) { + efree(resolved_path); + } + } + } + } + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + return accelerator_orig_zend_stream_open_function(filename, handle TSRMLS_CC); +} + +#else + +/* zend_stream_open_function() replacement for PHP 5.3 and above */ +static int persistent_stream_open_function(const char *filename, zend_file_handle *handle TSRMLS_DC) +{ + if (ZCG(enabled) && accel_startup_ok && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !CG(interactive) && + !ZCSG(restart_in_progress)) { + + /* check if callback is called from include_once or it's a main request */ + if ((!EG(opline_ptr) && + filename == SG(request_info).path_translated) || + (EG(opline_ptr) && + *EG(opline_ptr) && + (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL && +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) { +#else + ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) { +#endif + + /* we are in include_once or FastCGI request */ + zend_persistent_script *persistent_script; + + handle->filename = (char*)filename; + handle->free_filename = 0; + + /* check if cached script was already found by resolve_path() */ + if ((EG(opline_ptr) == NULL && + ZCG(cache_opline) == NULL && + ZCG(cache_persistent_script) != NULL) || + (EG(opline_ptr) && + (ZCG(cache_opline) == *EG(opline_ptr)))) { + persistent_script = ZCG(cache_persistent_script); + handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len); + handle->type = ZEND_HANDLE_FILENAME; + return SUCCESS; +#if 0 + } else { + /* FIXME: It looks like this part is not needed any more */ + int filename_len = strlen(filename); + + if ((IS_ABSOLUTE_PATH(filename, filename_len) || + is_stream_path(filename)) && + (persistent_script = zend_accel_hash_find(&ZCSG(hash), (char*)filename, filename_len + 1)) != NULL && + !persistent_script->corrupted) { + + handle->opened_path = estrndup(persistent_script->full_path, persistent_script->full_path_len); + handle->type = ZEND_HANDLE_FILENAME; + memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len + 1); + ZCG(key_len) = persistent_script->full_path_len; + ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL; + ZCG(cache_persistent_script) = EG(opline_ptr) ? persistent_script : NULL; + return SUCCESS; + } +#endif + } + } + } + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + return accelerator_orig_zend_stream_open_function(filename, handle TSRMLS_CC); +} + +/* zend_resolve_path() replacement for PHP 5.3 and above */ +static char* persistent_zend_resolve_path(const char *filename, int filename_len TSRMLS_DC) +{ + if (ZCG(enabled) && accel_startup_ok && + (ZCG(counted) || ZCSG(accelerator_enabled)) && + !CG(interactive) && + !ZCSG(restart_in_progress)) { + + /* check if callback is called from include_once or it's a main request */ + if ((!EG(opline_ptr) && + filename == SG(request_info).path_translated) || + (EG(opline_ptr) && + *EG(opline_ptr) && + (*EG(opline_ptr))->opcode == ZEND_INCLUDE_OR_EVAL && +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ((*EG(opline_ptr))->extended_value == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->extended_value == ZEND_REQUIRE_ONCE))) { +#else + ((*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_INCLUDE_ONCE || + (*EG(opline_ptr))->op2.u.constant.value.lval == ZEND_REQUIRE_ONCE))) { +#endif + + /* we are in include_once or FastCGI request */ + zend_file_handle handle; + char *key = NULL; + int key_length; + char *resolved_path; + zend_accel_hash_entry *bucket; + zend_persistent_script *persistent_script; + + /* Check if requested file already cached (by full name) */ + if ((IS_ABSOLUTE_PATH(filename, filename_len) || + is_stream_path(filename)) && + (bucket = zend_accel_hash_find_entry(&ZCSG(hash), (char*)filename, filename_len + 1)) != NULL) { + persistent_script = (zend_persistent_script *)bucket->data; + if (persistent_script && !persistent_script->corrupted) { + memcpy(ZCG(key), persistent_script->full_path, persistent_script->full_path_len + 1); + ZCG(key_len) = persistent_script->full_path_len; + ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL; + ZCG(cache_persistent_script) = persistent_script; + return estrndup(persistent_script->full_path, persistent_script->full_path_len); + } + } + + /* Check if requested file already cached (by key) */ + handle.filename = (char*)filename; + handle.free_filename = 0; + handle.opened_path = NULL; + key = accel_make_persistent_key_ex(&handle, filename_len, &key_length TSRMLS_CC); + if (!ZCG(accel_directives).revalidate_path && + key && + (persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length + 1)) != NULL && + !persistent_script->corrupted) { + + /* we have persistent script */ + ZCG(cache_opline) = EG(opline_ptr) ? *EG(opline_ptr) : NULL; + ZCG(cache_persistent_script) = persistent_script; + return estrndup(persistent_script->full_path, persistent_script->full_path_len); + } + + /* find the full real path */ + resolved_path = accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC); + + /* Check if requested file already cached (by real path) */ + if (resolved_path && + (bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path, strlen(resolved_path) + 1)) != NULL) { + persistent_script = (zend_persistent_script *)bucket->data; + + if (persistent_script && !persistent_script->corrupted) { + if (key && !ZCG(accel_directives).revalidate_path) { + /* add another "key" for the same bucket */ + SHM_UNPROTECT(); + zend_shared_alloc_lock(TSRMLS_C); + zend_accel_add_key(key, key_length, bucket TSRMLS_CC); + zend_shared_alloc_unlock(TSRMLS_C); + SHM_PROTECT(); + } + ZCG(cache_opline) = (EG(opline_ptr) && key) ? *EG(opline_ptr): NULL; + ZCG(cache_persistent_script) = key ? persistent_script : NULL; + return resolved_path; + } + } + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + return resolved_path; + } + } + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + return accelerator_orig_zend_resolve_path(filename, filename_len TSRMLS_CC); +} + +#endif + +static void zend_reset_cache_vars(TSRMLS_D) +{ + ZSMMG(memory_exhausted) = 0; + ZCSG(hits) = 0; + ZCSG(misses) = 0; + ZCSG(blacklist_misses) = 0; + ZSMMG(wasted_shared_memory) = 0; + ZCSG(restart_pending) = 0; + ZCSG(force_restart_time) = 0; +} + +static void accel_activate(void) +{ + TSRMLS_FETCH(); + + if (!ZCG(enabled) || !accel_startup_ok) { + return; + } + + SHM_UNPROTECT(); + /* PHP-5.4 and above return "double", but we use 1 sec precision */ + ZCG(request_time) = (time_t)sapi_get_request_time(TSRMLS_C); + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + ZCG(include_path_check) = !ZCG(include_path_key); + + if (ZCG(counted)) { +#ifdef ZTS + zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %d", tsrm_thread_id()); +#else + zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid()); +#endif + accel_unlock_all(TSRMLS_C); + ZCG(counted) = 0; + } + + if (ZCSG(restart_pending)) { + zend_shared_alloc_lock(TSRMLS_C); + if (ZCSG(restart_pending) != 0) { /* check again, to ensure that the cache wasn't already cleaned by another process */ + if (accel_is_inactive(TSRMLS_C) == SUCCESS) { + zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!"); + ZCSG(restart_pending) = 0; + switch ZCSG(restart_reason) { + case ACCEL_RESTART_OOM: + ZCSG(oom_restarts)++; + break; + case ACCEL_RESTART_WASTED: + ZCSG(wasted_restarts)++; + break; + case ACCEL_RESTART_HASH: + ZCSG(hash_restarts)++; + break; + case ACCEL_RESTART_USER: + ZCSG(manual_restarts)++; + break; + } + accel_restart_enter(TSRMLS_C); + + zend_reset_cache_vars(TSRMLS_C); + zend_accel_hash_clean(&ZCSG(hash)); + + /* include_paths keeps only the first path */ + if (ZCSG(include_paths).num_entries > 1) { + ZCSG(include_paths).num_entries = 1; + ZCSG(include_paths).num_direct_entries = 1; + memset(ZCSG(include_paths).hash_table, 0, sizeof(zend_accel_hash_entry*) * ZCSG(include_paths).max_num_entries); + ZCSG(include_paths).hash_table[zend_inline_hash_func(ZCSG(include_paths).hash_entries[0].key, ZCSG(include_paths).hash_entries[0].key_length) % ZCSG(include_paths).max_num_entries] = &ZCSG(include_paths).hash_entries[0]; + } + +#if (ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO) && !defined(ZTS) + accel_interned_strings_restore_state(TSRMLS_C); +#endif + + zend_shared_alloc_restore_state(); + ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart); + ZCSG(last_restart_time) = ZCG(request_time); + accel_restart_leave(TSRMLS_C); + } + } + zend_shared_alloc_unlock(TSRMLS_C); + } + + /* check if ZCG(function_table) wasn't somehow polluted on the way */ + if (ZCG(internal_functions_count) != zend_hash_num_elements(&ZCG(function_table))) { + zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table))); + } + + if (ZCG(accel_directives).validate_timestamps) { + time_t now = ZCG(request_time); + if (now > ZCSG(revalidate_at) + (time_t)ZCG(accel_directives).revalidate_freq) { + ZCSG(revalidate_at) = now; + } + } + + ZCG(cwd) = NULL; + + SHM_PROTECT(); +} + +#if !ZEND_DEBUG + +/* Fast Request Shutdown + * ===================== + * Zend Memory Manager frees memory by its own. We don't have to free each + * allocated block separately, but we like to call all the destructors and + * callbacks in exactly the same order. + */ + +static void accel_fast_hash_destroy(HashTable *ht) +{ + Bucket *p = ht->pListHead; + + while (p != NULL) { + ht->pDestructor(p->pData); + p = p->pListNext; + } +} + +static void accel_fast_zval_ptr_dtor(zval **zval_ptr) +{ + zval *zvalue = *zval_ptr; + + if (Z_DELREF_P(zvalue) == 0) { +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + switch (Z_TYPE_P(zvalue) & IS_CONSTANT_TYPE_MASK) { +#else + switch (Z_TYPE_P(zvalue) & ~IS_CONSTANT_INDEX) { +#endif + case IS_ARRAY: + case IS_CONSTANT_ARRAY: { + TSRMLS_FETCH(); + + if (zvalue->value.ht && (zvalue->value.ht != &EG(symbol_table))) { + zvalue->value.ht->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + accel_fast_hash_destroy(zvalue->value.ht); + } + } + break; + case IS_OBJECT: + { + TSRMLS_FETCH(); + + Z_OBJ_HT_P(zvalue)->del_ref(zvalue TSRMLS_CC); + } + break; + case IS_RESOURCE: + { + TSRMLS_FETCH(); + + /* destroy resource */ + zend_list_delete(zvalue->value.lval); + } + break; + case IS_LONG: + case IS_DOUBLE: + case IS_BOOL: + case IS_NULL: + case IS_STRING: + case IS_CONSTANT: + default: + return; + break; + } + } +} + +static int accel_clean_non_persistent_function(zend_function *function TSRMLS_DC) +{ + if (function->type == ZEND_INTERNAL_FUNCTION) { + return ZEND_HASH_APPLY_STOP; + } else { + if (function->op_array.static_variables) { + function->op_array.static_variables->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + accel_fast_hash_destroy(function->op_array.static_variables); + function->op_array.static_variables = NULL; + } + return (--(*function->op_array.refcount) <= 0) ? + ZEND_HASH_APPLY_REMOVE : + ZEND_HASH_APPLY_KEEP; + } +} + +static int accel_cleanup_function_data(zend_function *function TSRMLS_DC) +{ + if (function->type == ZEND_USER_FUNCTION) { + if (function->op_array.static_variables) { + function->op_array.static_variables->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + accel_fast_hash_destroy(function->op_array.static_variables); + function->op_array.static_variables = NULL; + } + } + return 0; +} + +static int accel_clean_non_persistent_class(zend_class_entry **pce TSRMLS_DC) +{ + zend_class_entry *ce = *pce; + + if (ce->type == ZEND_INTERNAL_CLASS) { + return ZEND_HASH_APPLY_STOP; + } else { +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { + zend_hash_apply(&ce->function_table, (apply_func_t) accel_cleanup_function_data TSRMLS_CC); + } + if (ce->static_members_table) { + int i; + + for (i = 0; i < ce->default_static_members_count; i++) { + if (ce->static_members_table[i]) { + accel_fast_zval_ptr_dtor(&ce->static_members_table[i]); + ce->static_members_table[i] = NULL; + } + } + ce->static_members_table = NULL; + } +#else + zend_hash_apply(&ce->function_table, (apply_func_t) accel_cleanup_function_data TSRMLS_CC); + if (ce->static_members) { + ce->static_members->pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + accel_fast_hash_destroy(ce->static_members); + ce->static_members = NULL; + } +#endif + return ZEND_HASH_APPLY_REMOVE; + } +} + +static int accel_clean_non_persistent_constant(zend_constant *c TSRMLS_DC) +{ + if (c->flags & CONST_PERSISTENT) { + return ZEND_HASH_APPLY_STOP; + } else { + interned_free(c->name); + return ZEND_HASH_APPLY_REMOVE; + } +} + +static void zend_accel_fast_shutdown(TSRMLS_D) +{ + if (EG(full_tables_cleanup)) { + EG(symbol_table).pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + } else { + dtor_func_t old_destructor; + + if (EG(objects_store).top > 1 || zend_hash_num_elements(&EG(regular_list)) > 0) { + /* We don't have to destroy all zvals if they cannot call any destructors */ + + old_destructor = EG(symbol_table).pDestructor; + EG(symbol_table).pDestructor = (dtor_func_t)accel_fast_zval_ptr_dtor; + zend_try { + zend_hash_graceful_reverse_destroy(&EG(symbol_table)); + } zend_end_try(); + EG(symbol_table).pDestructor = old_destructor; + } + zend_hash_init(&EG(symbol_table), 0, NULL, NULL, 0); + old_destructor = EG(function_table)->pDestructor; + EG(function_table)->pDestructor = NULL; + zend_hash_reverse_apply(EG(function_table), (apply_func_t) accel_clean_non_persistent_function TSRMLS_CC); + EG(function_table)->pDestructor = old_destructor; + old_destructor = EG(class_table)->pDestructor; + EG(class_table)->pDestructor = NULL; + zend_hash_reverse_apply(EG(class_table), (apply_func_t) accel_clean_non_persistent_class TSRMLS_CC); + EG(class_table)->pDestructor = old_destructor; + old_destructor = EG(zend_constants)->pDestructor; + EG(zend_constants)->pDestructor = NULL; + zend_hash_reverse_apply(EG(zend_constants), (apply_func_t) accel_clean_non_persistent_constant TSRMLS_CC); + EG(zend_constants)->pDestructor = old_destructor; + } + CG(unclean_shutdown) = 1; +} +#endif + +static void accel_deactivate(void) +{ + /* ensure that we restore function_table and class_table + * In general, they're restored by persistent_compile_file(), but in case + * the script is aborted abnormally, they may become messed up. + */ + TSRMLS_FETCH(); + + if (!ZCG(enabled) || !accel_startup_ok) { + return; + } + + zend_shared_alloc_safe_unlock(TSRMLS_C); /* be sure we didn't leave cache locked */ + accel_unlock_all(TSRMLS_C); + ZCG(counted) = 0; + +#if !ZEND_DEBUG + if (ZCG(accel_directives).fast_shutdown) { + zend_accel_fast_shutdown(TSRMLS_C); + } +#endif + + if (ZCG(cwd)) { + efree(ZCG(cwd)); + ZCG(cwd) = NULL; + } + +} + +static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2) +{ + (void)element2; /* keep the compiler happy */ + + if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) { + element1->startup = NULL; +#if 0 + /* We have to call shutdown callback it to free TS resources */ + element1->shutdown = NULL; +#endif + element1->activate = NULL; + element1->deactivate = NULL; + element1->op_array_handler = NULL; + +#ifdef __DEBUG_MESSAGES__ + fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error")); + fflush(stderr); +#endif + } + + return 0; +} + +static void zps_startup_failure(char *reason, char *api_reason, int (*cb)(zend_extension *, zend_extension *) TSRMLS_DC) +{ + accel_startup_ok = 0; + zps_failure_reason = reason; + zps_api_failure_reason = api_reason?api_reason:reason; + zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb); +} + +static inline int accel_find_sapi(TSRMLS_D) +{ + static const char *supported_sapis[] = { + "apache", + "fastcgi", + "cli-server", + "cgi-fcgi", + "fpm-fcgi", + "isapi", + "apache2filter", + "apache2handler", + NULL + }; + const char **sapi_name; + + if (sapi_module.name) { + for (sapi_name = supported_sapis; *sapi_name; sapi_name++) { + if (strcmp(sapi_module.name, *sapi_name) == 0) { + return SUCCESS; + } + } + if (ZCG(accel_directives).enable_cli && + strcmp(sapi_module.name, "cli") == 0) { + return SUCCESS; + } + } + + return FAILURE; +} + +static void zend_accel_init_shm(TSRMLS_D) +{ + zend_shared_alloc_lock(TSRMLS_C); + + accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals)); + if (!accel_shared_globals) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + return; + } + ZSMMG(app_shared_globals) = accel_shared_globals; + + zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files); + zend_accel_hash_init(&ZCSG(include_paths), 32); + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + +# ifndef ZTS + zend_hash_init(&ZCSG(interned_strings), (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024) / (sizeof(Bucket) + sizeof(Bucket*) + 8 /* average string length */), NULL, NULL, 1); + ZCSG(interned_strings).nTableMask = ZCSG(interned_strings).nTableSize - 1; + ZCSG(interned_strings).arBuckets = zend_shared_alloc(ZCSG(interned_strings).nTableSize * sizeof(Bucket *)); + ZCSG(interned_strings_start) = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024)); + if (!ZCSG(interned_strings).arBuckets || !ZCSG(interned_strings_start)) { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " cannot allocate buffer for interned strings"); + } + ZCSG(interned_strings_end) = ZCSG(interned_strings_start) + (ZCG(accel_directives).interned_strings_buffer * 1024 * 1024); + ZCSG(interned_strings_top) = ZCSG(interned_strings_start); +# else + ZCSG(interned_strings_start) = ZCSG(interned_strings_end) = NULL; +# endif + + orig_interned_strings_start = CG(interned_strings_start); + orig_interned_strings_end = CG(interned_strings_end); + orig_new_interned_string = zend_new_interned_string; + orig_interned_strings_snapshot = zend_interned_strings_snapshot; + orig_interned_strings_restore = zend_interned_strings_restore; + + CG(interned_strings_start) = ZCSG(interned_strings_start); + CG(interned_strings_end) = ZCSG(interned_strings_end); + zend_new_interned_string = accel_new_interned_string_for_php; + zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php; + zend_interned_strings_restore = accel_interned_strings_restore_for_php; + +# ifndef ZTS + accel_use_shm_interned_strings(TSRMLS_C); + accel_interned_strings_save_state(TSRMLS_C); +# endif + +#endif + + zend_reset_cache_vars(TSRMLS_C); + + ZCSG(oom_restarts) = 0; + ZCSG(wasted_restarts) = 0; + ZCSG(hash_restarts) = 0; + ZCSG(manual_restarts) = 0; + + ZCSG(accelerator_enabled) = 1; + ZCSG(last_restart_time) = zend_accel_get_time(); + ZCSG(restart_in_progress) = 0; + + zend_shared_alloc_unlock(TSRMLS_C); +} + +static void accel_globals_ctor(zend_accel_globals *accel_globals TSRMLS_DC) +{ + memset(accel_globals, 0, sizeof(zend_accel_globals)); + zend_hash_init(&accel_globals->function_table, zend_hash_num_elements(CG(function_table)), NULL, ZEND_FUNCTION_DTOR, 1); + zend_accel_copy_internal_functions(TSRMLS_C); +} + +static void accel_globals_dtor(zend_accel_globals *accel_globals TSRMLS_DC) +{ + accel_globals->function_table.pDestructor = NULL; + zend_hash_destroy(&accel_globals->function_table); +} + +static int accel_startup(zend_extension *extension) +{ + zend_function *func; + zend_ini_entry *ini_entry; + TSRMLS_FETCH(); + +#ifdef ZTS + accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor); +#else + accel_globals_ctor(&accel_globals); +#endif + +#ifdef ZEND_WIN32 + _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */ +#endif + + if (start_accel_module() == FAILURE) { + accel_startup_ok = 0; + zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!"); + return FAILURE; + } + + /* no supported SAPI found - disable acceleration and stop initialization */ + if (accel_find_sapi(TSRMLS_C) == FAILURE) { + accel_startup_ok = 0; + if (!ZCG(accel_directives).enable_cli && + strcmp(sapi_module.name, "cli") == 0) { + zps_startup_failure("Opcode Caching is disabled for CLI", NULL, accelerator_remove_cb TSRMLS_CC); + } else { + zps_startup_failure("Opcode Caching is only supported in Apache, ISAPI, FPM and FastCGI SAPIs", NULL, accelerator_remove_cb TSRMLS_CC); + } + return SUCCESS; + } + + if (ZCG(enabled) == 0) { + return SUCCESS ; + } +/********************************************/ +/* End of non-SHM dependent initializations */ +/********************************************/ + switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) { + case ALLOC_SUCCESS: + zend_accel_init_shm(TSRMLS_C); + break; + case ALLOC_FAILURE: + accel_startup_ok = 0; + zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory."); + return SUCCESS; + case SUCCESSFULLY_REATTACHED: + accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + zend_shared_alloc_lock(TSRMLS_C); + orig_interned_strings_start = CG(interned_strings_start); + orig_interned_strings_end = CG(interned_strings_end); + orig_new_interned_string = zend_new_interned_string; + orig_interned_strings_snapshot = zend_interned_strings_snapshot; + orig_interned_strings_restore = zend_interned_strings_restore; + + CG(interned_strings_start) = ZCSG(interned_strings_start); + CG(interned_strings_end) = ZCSG(interned_strings_end); + zend_new_interned_string = accel_new_interned_string_for_php; + zend_interned_strings_snapshot = accel_interned_strings_snapshot_for_php; + zend_interned_strings_restore = accel_interned_strings_restore_for_php; + +# ifndef ZTS + accel_use_shm_interned_strings(TSRMLS_C); +# endif + + zend_shared_alloc_unlock(TSRMLS_C); +#endif + + break; + case FAILED_REATTACHED: + accel_startup_ok = 0; + zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory."); + return SUCCESS; + break; + } + + /* from this point further, shared memory is supposed to be OK */ + + /* Override compiler */ + accelerator_orig_compile_file = zend_compile_file; + zend_compile_file = persistent_compile_file; + + /* Override stream opener function (to eliminate open() call caused by + * include/require statements ) */ + accelerator_orig_zend_stream_open_function = zend_stream_open_function; + zend_stream_open_function = persistent_stream_open_function; + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + /* Override path resolver function (to eliminate stat() calls caused by + * include_once/require_once statements */ + accelerator_orig_zend_resolve_path = zend_resolve_path; + zend_resolve_path = persistent_zend_resolve_path; +#endif + + if (ZCG(accel_directives).validate_timestamps) { + ZCSG(revalidate_at) = zend_accel_get_time() + ZCG(accel_directives).revalidate_freq; + } + + /* Override chdir() function */ + if (zend_hash_find(CG(function_table), "chdir", sizeof("chdir"), (void**)&func) == SUCCESS && + func->type == ZEND_INTERNAL_FUNCTION) { + orig_chdir = func->internal_function.handler; + func->internal_function.handler = ZEND_FN(accel_chdir); + } + ZCG(cwd) = NULL; + + /* Override "include_path" modifier callback */ + if (zend_hash_find(EG(ini_directives), "include_path", sizeof("include_path"), (void **) &ini_entry) == SUCCESS) { + ZCG(include_path) = INI_STR("include_path"); + ZCG(include_path_key) = NULL; + if (ZCG(include_path) && *ZCG(include_path)) { + ZCG(include_path_len) = strlen(ZCG(include_path)); + if (!zend_accel_hash_is_full(&ZCSG(include_paths))) { + char *key; + + zend_shared_alloc_lock(TSRMLS_C); + key = zend_shared_alloc(ZCG(include_path_len) + 2); + if (key) { + memcpy(key, ZCG(include_path), ZCG(include_path_len) + 1); + key[ZCG(include_path_len) + 1] = 'A' + ZCSG(include_paths).num_entries; + ZCG(include_path_key) = key + ZCG(include_path_len) + 1; + zend_accel_hash_update(&ZCSG(include_paths), key, ZCG(include_path_len) + 1, 0, ZCG(include_path_key)); + } + zend_shared_alloc_unlock(TSRMLS_C); + } + } else { + ZCG(include_path) = ""; + ZCG(include_path_len) = 0; + } + orig_include_path_on_modify = ini_entry->on_modify; + ini_entry->on_modify = accel_include_path_on_modify; + } + + zend_shared_alloc_lock(TSRMLS_C); + zend_shared_alloc_save_state(); + zend_shared_alloc_unlock(TSRMLS_C); + + SHM_PROTECT(); + + accel_startup_ok = 1; + + /* Override file_exists(), is_file() and is_readable() */ + zend_accel_override_file_functions(TSRMLS_C); + + /* Load black list */ + accel_blacklist.entries = NULL; + if (ZCG(enabled) && accel_startup_ok && + ZCG(accel_directives).user_blacklist_filename && + *ZCG(accel_directives.user_blacklist_filename)) { + zend_accel_blacklist_init(&accel_blacklist); + zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename)); + } + +#if 0 + /* FIXME: We probably don't need it here */ + zend_accel_copy_internal_functions(TSRMLS_C); +#endif + + return SUCCESS; +} + +static void accel_free_ts_resources() +{ +#ifndef ZTS + accel_globals_dtor(&accel_globals); +#else + ts_free_id(accel_globals_id); +#endif +} + +static void accel_shutdown(zend_extension *extension) +{ + zend_ini_entry *ini_entry; + TSRMLS_FETCH(); + + (void)extension; /* keep the compiler happy */ + + zend_accel_blacklist_shutdown(&accel_blacklist); + + if (!ZCG(enabled) || !accel_startup_ok) { + accel_free_ts_resources(); + return; + } + + accel_free_ts_resources(); + zend_shared_alloc_shutdown(); + zend_compile_file = accelerator_orig_compile_file; + + if (zend_hash_find(EG(ini_directives), "include_path", sizeof("include_path"), (void **) &ini_entry) == SUCCESS) { + ini_entry->on_modify = orig_include_path_on_modify; + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + CG(interned_strings_start) = orig_interned_strings_start; + CG(interned_strings_end) = orig_interned_strings_end; + zend_new_interned_string = orig_new_interned_string; + zend_interned_strings_snapshot = orig_interned_strings_snapshot; + zend_interned_strings_restore = orig_interned_strings_restore; +#endif + +} + +void zend_accel_schedule_restart(zend_accel_restart_reason reason TSRMLS_DC) +{ + if (ZCSG(restart_pending)) { + /* don't schedule twice */ + return; + } + zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled!"); + + ZCSG(restart_pending) = 1; + ZCSG(restart_reason) = reason; + ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled); + ZCSG(accelerator_enabled) = 0; + + if (ZCG(accel_directives).force_restart_timeout) { + ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout; + } else { + ZCSG(force_restart_time) = 0; + } +} + +/* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */ +#ifdef ZEND_WIN32 +#define accel_deactivate_now() ZCG(counted) = 1; accel_deactivate_sub(TSRMLS_C) +#else +#define accel_deactivate_now() accel_deactivate_sub(TSRMLS_C) +#endif + +/* ensures it is OK to read SHM + if it's not OK (restart in progress) returns FAILURE + if OK returns SUCCESS + MUST call accelerator_shm_read_unlock after done lock operations +*/ +int accelerator_shm_read_lock(TSRMLS_D) +{ + if (ZCG(counted)) { + /* counted means we are holding read lock for SHM, so that nothing bad can happen */ + return SUCCESS; + } else { + /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled + or is in progress now */ + accel_activate_add(TSRMLS_C); /* acquire usage lock */ + /* Now if we weren't inside restart, restart would not begin until we remove usage lock */ + if (ZCSG(restart_in_progress)) { + /* we already were inside restart this means it's not safe to touch shm */ + accel_deactivate_now(); /* drop usage lock */ + return FAILURE; + } + } + return SUCCESS; +} + +/* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */ +void accelerator_shm_read_unlock(TSRMLS_D) +{ + if (!ZCG(counted)) { + /* counted is 0 - meaning we had to readlock manually, release readlock now */ + accel_deactivate_now(); + } +} + +static void accel_op_array_handler(zend_op_array *op_array) +{ + TSRMLS_FETCH(); + + if (ZCG(enabled) && accel_startup_ok && ZCSG(accelerator_enabled)) { + zend_optimizer(op_array TSRMLS_CC); + } +} + +ZEND_EXT_API zend_extension zend_extension_entry = { + ACCELERATOR_PRODUCT_NAME, /* name */ + ACCELERATOR_VERSION, /* version */ + "Zend Technologies", /* author */ + "http://www.zend.com/", /* URL */ + "Copyright (c) 1999-2013", /* copyright */ + accel_startup, /* startup */ + accel_shutdown, /* shutdown */ + accel_activate, /* per-script activation */ + accel_deactivate, /* per-script deactivation */ + NULL, /* message handler */ + accel_op_array_handler, /* op_array handler */ + NULL, /* extended statement handler */ + NULL, /* extended fcall begin handler */ + NULL, /* extended fcall end handler */ + NULL, /* op_array ctor */ + NULL, /* op_array dtor */ + STANDARD_ZEND_EXTENSION_PROPERTIES +}; diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h new file mode 100644 index 000000000..3f10630ac --- /dev/null +++ b/ext/opcache/ZendAccelerator.h @@ -0,0 +1,382 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_H +#define ZEND_ACCELERATOR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#define ACCELERATOR_PRODUCT_NAME "Zend OPcache" +#define ACCELERATOR_VERSION "7.0.1-dev" +/* 2 - added Profiler support, on 20010712 */ +/* 3 - added support for Optimizer's encoded-only-files mode */ +/* 4 - works with the new Optimizer, that supports the file format with licenses */ +/* 5 - API 4 didn't really work with the license-enabled file format. v5 does. */ +/* 6 - Monitor was removed from ZendPlatform.so, to a module of its own */ +/* 7 - Optimizer was embedded into Accelerator */ +/* 8 - Standalone Open Source Zend OPcache */ +#define ACCELERATOR_API_NO 8 + +#if ZEND_WIN32 +# include "zend_config.w32.h" +#else +#include "zend_config.h" +# include <sys/time.h> +# include <sys/resource.h> +#endif + +#if HAVE_UNISTD_H +# include "unistd.h" +#endif + +#include "zend_extensions.h" +#include "zend_compile.h" + +#include "Optimizer/zend_optimizer.h" +#include "zend_accelerator_hash.h" +#include "zend_accelerator_debug.h" + +#ifndef PHPAPI +# ifdef ZEND_WIN32 +# define PHPAPI __declspec(dllimport) +# else +# define PHPAPI +# endif +#endif + +#ifndef ZEND_EXT_API +# if WIN32|WINNT +# define ZEND_EXT_API __declspec(dllexport) +# elif defined(__GNUC__) && __GNUC__ >= 4 +# define ZEND_EXT_API __attribute__ ((visibility("default"))) +# else +# define ZEND_EXT_API +# endif +#endif + +#ifdef ZEND_WIN32 +# ifndef MAXPATHLEN +# define MAXPATHLEN _MAX_PATH +# endif +# include <direct.h> +#else +# include <sys/param.h> +#endif + +#define PHP_5_0_X_API_NO 220040412 +#define PHP_5_1_X_API_NO 220051025 +#define PHP_5_2_X_API_NO 220060519 +#define PHP_5_3_X_API_NO 220090626 +#define PHP_5_4_X_API_NO 220100525 + +/*** file locking ***/ +#ifndef ZEND_WIN32 +extern int lock_file; + +# if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)/* Darwin */) || defined(__OpenBSD__) || defined(__NetBSD__) +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {start, len, -1, type, whence} +# elif defined(__svr4__) +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {type, whence, start, len} +# elif defined(__linux__) || defined(__hpux) +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {type, whence, start, len, 0} +# elif defined(_AIX) +# if defined(_LARGE_FILES) || defined(__64BIT__) +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {type, whence, 0, 0, 0, start, len } +# else +# define FLOCK_STRUCTURE(name, type, whence, start, len) \ + struct flock name = {type, whence, start, len} +# endif +# else +# error "Don't know how to define struct flock" +# endif +#endif + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + #define Z_REFCOUNT_P(pz) (pz)->refcount + #define Z_SET_REFCOUNT_P(pz, v) (pz)->refcount = (v) + #define Z_ADDREF_P(pz) ++((pz)->refcount) + #define Z_DELREF_P(pz) --((pz)->refcount) + #define Z_ISREF_P(pz) (pz)->is_ref + #define Z_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1) + #define Z_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0) + #define Z_SET_ISREF_TO_P(pz, isref) (pz)->is_ref = (isref) + #define PZ_REFCOUNT_P(pz) (pz)->refcount + #define PZ_SET_REFCOUNT_P(pz, v) (pz)->refcount = (v) + #define PZ_ADDREF_P(pz) ++((pz)->refcount) + #define PZ_DELREF_P(pz) --((pz)->refcount) + #define PZ_ISREF_P(pz) (pz)->is_ref + #define PZ_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1) + #define PZ_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0) + #define PZ_SET_ISREF_TO_P(pz, isref) (pz)->is_ref = (isref) +#else + #define PZ_REFCOUNT_P(pz) (pz)->refcount__gc + #define PZ_SET_REFCOUNT_P(pz, v) (pz)->refcount__gc = (v) + #define PZ_ADDREF_P(pz) ++((pz)->refcount__gc) + #define PZ_DELREF_P(pz) --((pz)->refcount__gc) + #define PZ_ISREF_P(pz) (pz)->is_ref__gc + #define PZ_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1) + #define PZ_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0) + #define PZ_SET_ISREF_TO_P(pz, isref) (pz)->is_ref__gc = (isref) +#endif + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO +# ifdef ALLOCA_FLAG + #define DO_ALLOCA(x) do_alloca_with_limit(x, use_heap) + #define FREE_ALLOCA(x) free_alloca_with_limit(x, use_heap) +# else + #define ALLOCA_FLAG(x) + #define DO_ALLOCA(x) do_alloca(x) + #define FREE_ALLOCA(x) free_alloca(x) +# endif +#else + #define DO_ALLOCA(x) do_alloca(x, use_heap) + #define FREE_ALLOCA(x) free_alloca(x, use_heap) +#endif + + +#if ZEND_WIN32 +typedef unsigned __int64 accel_time_t; +#else +typedef time_t accel_time_t; +#endif + +typedef enum _zend_accel_restart_reason { + ACCEL_RESTART_OOM, /* restart because of out of memory */ + ACCEL_RESTART_WASTED, /* restart because of wasted memory */ + ACCEL_RESTART_HASH, /* restart because of hash overflow */ + ACCEL_RESTART_USER /* restart sheduled by opcache_reset() */ +} zend_accel_restart_reason; + +typedef struct _zend_persistent_script { + ulong hash_value; + char *full_path; /* full real path with resolved symlinks */ + unsigned int full_path_len; + zend_op_array main_op_array; + HashTable function_table; + HashTable class_table; + long compiler_halt_offset; /* position of __HALT_COMPILER or -1 */ + int ping_auto_globals_mask; /* which autoglobals are used by the script */ + accel_time_t timestamp; /* the script modification time */ + zend_bool corrupted; +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zend_uint early_binding; /* the linked list of delayed declarations */ +#endif + + void *mem; /* shared memory area used by script structures */ + size_t size; /* size of used shared memory */ + + /* All entries that shouldn't be counted in the ADLER32 + * checksum must be declared in this struct + */ + struct zend_persistent_script_dynamic_members { + time_t last_used; + ulong hits; + unsigned int memory_consumption; + unsigned int checksum; + time_t revalidate; + } dynamic_members; +} zend_persistent_script; + +typedef struct _zend_accel_directives { + long memory_consumption; + long max_accelerated_files; + double max_wasted_percentage; + char *user_blacklist_filename; + long consistency_checks; + long force_restart_timeout; + zend_bool use_cwd; + zend_bool ignore_dups; + zend_bool validate_timestamps; + zend_bool revalidate_path; + zend_bool save_comments; + zend_bool load_comments; + zend_bool fast_shutdown; + zend_bool protect_memory; + zend_bool file_override_enabled; + zend_bool inherited_hack; + zend_bool enable_cli; + unsigned long revalidate_freq; + char *error_log; +#ifdef ZEND_WIN32 + char *mmap_base; +#endif + char *memory_model; + long log_verbosity_level; + + long optimization_level; + long max_file_size; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + long interned_strings_buffer; +#endif +} zend_accel_directives; + +typedef struct _zend_accel_globals { + /* copy of CG(function_table) used for compilation scripts into cache */ + /* initially it contains only internal functions */ + HashTable function_table; + int internal_functions_count; + int counted; /* the process uses shared memory */ + zend_bool enabled; + zend_bool locked; /* thread obtained exclusive lock */ + HashTable bind_hash; /* prototype and zval lookup table */ + zend_accel_directives accel_directives; + char *cwd; /* current working directory or NULL */ + int cwd_len; /* "cwd" string length */ + char *include_path_key; /* one letter key of current "include_path" */ + char *include_path; /* current section of "include_path" directive */ + int include_path_len; /* "include_path" string length */ + int include_path_check; + time_t request_time; + /* preallocated shared-memory block to save current script */ + void *mem; + /* cache to save hash lookup on the same INCLUDE opcode */ + zend_op *cache_opline; + zend_persistent_script *cache_persistent_script; + /* preallocated buffer for keys */ + int key_len; + char key[MAXPATHLEN * 8]; +} zend_accel_globals; + +typedef struct _zend_accel_shared_globals { + /* Cache Data Structures */ + unsigned long hits; + unsigned long misses; + unsigned long blacklist_misses; + unsigned long oom_restarts; /* number of restarts because of out of memory */ + unsigned long wasted_restarts; /* number of restarts because of wasted memory */ + unsigned long hash_restarts; /* number of restarts because of hash overflow */ + unsigned long manual_restarts; /* number of restarts sheduled by opcache_reset() */ + zend_accel_hash hash; /* hash table for cached scripts */ + zend_accel_hash include_paths; /* used "include_path" values */ + + /* Directives & Maintenance */ + time_t last_restart_time; + time_t force_restart_time; + zend_bool accelerator_enabled; + zend_bool restart_pending; + zend_accel_restart_reason restart_reason; + zend_bool cache_status_before_restart; +#ifdef ZEND_WIN32 + unsigned long mem_usage; + unsigned long restart_in; +#endif + zend_bool restart_in_progress; + time_t revalidate_at; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + /* Interned Strings Support */ + char *interned_strings_start; + char *interned_strings_top; + char *interned_strings_end; + HashTable interned_strings; + struct { + Bucket **arBuckets; + Bucket *pListHead; + Bucket *pListTail; + char *top; + } interned_strings_saved_state; +#endif +} zend_accel_shared_globals; + +extern zend_bool accel_startup_ok; + +extern zend_accel_shared_globals *accel_shared_globals; +#define ZCSG(element) (accel_shared_globals->element) + +#ifdef ZTS +# define ZCG(v) TSRMG(accel_globals_id, zend_accel_globals *, v) +extern int accel_globals_id; +#else +# define ZCG(v) (accel_globals.v) +extern zend_accel_globals accel_globals; +#endif + +extern char *zps_api_failure_reason; + +void zend_accel_schedule_restart(zend_accel_restart_reason reason TSRMLS_DC); +int accelerator_shm_read_lock(TSRMLS_D); +void accelerator_shm_read_unlock(TSRMLS_D); + +char *accel_make_persistent_key_ex(zend_file_handle *file_handle, int path_length, int *key_len TSRMLS_DC); + +#if !defined(ZEND_DECLARE_INHERITED_CLASS_DELAYED) +# define ZEND_DECLARE_INHERITED_CLASS_DELAYED 145 +#endif + +#define ZEND_DECLARE_INHERITED_CLASS_DELAYED_FLAG 0x80 + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + +const char *accel_new_interned_string(const char *arKey, int nKeyLength, int free_src TSRMLS_DC); + +# define interned_free(s) do { \ + if (!IS_INTERNED(s)) { \ + free(s); \ + } \ + } while (0) +# define interned_efree(s) do { \ + if (!IS_INTERNED(s)) { \ + efree(s); \ + } \ + } while (0) +# define interned_estrndup(s, n) \ + (IS_INTERNED(s) ? (s) : estrndup(s, n)) +# define ZEND_RESULT_TYPE(opline) (opline)->result_type +# define ZEND_RESULT(opline) (opline)->result +# define ZEND_OP1_TYPE(opline) (opline)->op1_type +# define ZEND_OP1(opline) (opline)->op1 +# define ZEND_OP1_CONST(opline) (*(opline)->op1.zv) +# define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant].constant +# define ZEND_OP2_TYPE(opline) (opline)->op2_type +# define ZEND_OP2(opline) (opline)->op2 +# define ZEND_OP2_CONST(opline) (*(opline)->op2.zv) +# define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant].constant +# define ZEND_DONE_PASS_TWO(op_array) (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0) +# define ZEND_CE_FILENAME(ce) (ce)->info.user.filename +# define ZEND_CE_DOC_COMMENT(ce) (ce)->info.user.doc_comment +# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->info.user.doc_comment_len +#else +# define IS_INTERNED(s) 0 +# define interned_free(s) free(s) +# define interned_efree(s) efree(s) +# define interned_estrndup(s, n) estrndup(s, n) +# define ZEND_RESULT_TYPE(opline) (opline)->result.op_type +# define ZEND_RESULT(opline) (opline)->result.u +# define ZEND_OP1_TYPE(opline) (opline)->op1.op_type +# define ZEND_OP1(opline) (opline)->op1.u +# define ZEND_OP1_CONST(opline) (opline)->op1.u.constant +# define ZEND_OP1_LITERAL(opline) (opline)->op1.u.constant +# define ZEND_OP2_TYPE(opline) (opline)->op2.op_type +# define ZEND_OP2(opline) (opline)->op2.u +# define ZEND_OP2_CONST(opline) (opline)->op2.u.constant +# define ZEND_OP2_LITERAL(opline) (opline)->op2.u.constant +# define ZEND_DONE_PASS_TWO(op_array) ((op_array)->done_pass_two != 0) +# define ZEND_CE_FILENAME(ce) (ce)->filename +# define ZEND_CE_DOC_COMMENT(ce) (ce)->doc_comment +# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->doc_comment_len +#endif + +#endif /* ZEND_ACCELERATOR_H */ diff --git a/ext/opcache/config.m4 b/ext/opcache/config.m4 new file mode 100644 index 000000000..cbf7d5a19 --- /dev/null +++ b/ext/opcache/config.m4 @@ -0,0 +1,346 @@ +dnl +dnl $Id$ +dnl + +PHP_ARG_ENABLE(opcache, whether to enable Zend OPcache support, +[ --enable-opcache Enable Zend OPcache support], yes) + +if test "$PHP_OPCACHE" != "no"; then + + AC_CHECK_FUNC(mprotect,[ + AC_DEFINE(HAVE_MPROTECT, 1, [Define if you have mprotect() function]) + ]) + + AC_MSG_CHECKING(for sysvipc shared memory support) + AC_TRY_RUN([ +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <unistd.h> +#include <string.h> + +int main() { + pid_t pid; + int status; + int ipc_id; + char *shm; + struct shmid_ds shmbuf; + + ipc_id = shmget(IPC_PRIVATE, 4096, (IPC_CREAT | SHM_R | SHM_W)); + if (ipc_id == -1) { + return 1; + } + + shm = shmat(ipc_id, NULL, 0); + if (shm == (void *)-1) { + shmctl(ipc_id, IPC_RMID, NULL); + return 2; + } + + if (shmctl(ipc_id, IPC_STAT, &shmbuf) != 0) { + shmdt(shm); + shmctl(ipc_id, IPC_RMID, NULL); + return 3; + } + + shmbuf.shm_perm.uid = getuid(); + shmbuf.shm_perm.gid = getgid(); + shmbuf.shm_perm.mode = 0600; + + if (shmctl(ipc_id, IPC_SET, &shmbuf) != 0) { + shmdt(shm); + shmctl(ipc_id, IPC_RMID, NULL); + return 4; + } + + shmctl(ipc_id, IPC_RMID, NULL); + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_IPC, 1, [Define if you have SysV IPC SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + AC_MSG_CHECKING(for mmap() using MAP_ANON shared memory support) + AC_TRY_RUN([ +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <unistd.h> +#include <string.h> + +#ifndef MAP_ANON +# ifdef MAP_ANONYMOUS +# define MAP_ANON MAP_ANONYMOUS +# endif +#endif +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +int main() { + pid_t pid; + int status; + char *shm; + + shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (shm == MAP_FAILED) { + return 1; + } + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_MMAP_ANON, 1, [Define if you have mmap(MAP_ANON) SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + AC_MSG_CHECKING(for mmap() using /dev/zero shared memory support) + AC_TRY_RUN([ +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> + +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +int main() { + pid_t pid; + int status; + int fd; + char *shm; + + fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR); + if (fd == -1) { + return 1; + } + + shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (shm == MAP_FAILED) { + return 2; + } + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_MMAP_ZERO, 1, [Define if you have mmap("/dev/zero") SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + AC_MSG_CHECKING(for mmap() using shm_open() shared memory support) + AC_TRY_RUN([ +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +int main() { + pid_t pid; + int status; + int fd; + char *shm; + char tmpname[4096]; + + sprintf(tmpname,"test.shm.%dXXXXXX", getpid()); + if (mktemp(tmpname) == NULL) { + return 1; + } + fd = shm_open(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + return 2; + } + if (ftruncate(fd, 4096) < 0) { + close(fd); + shm_unlink(tmpname); + return 3; + } + + shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (shm == MAP_FAILED) { + return 4; + } + shm_unlink(tmpname); + close(fd); + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_MMAP_POSIX, 1, [Define if you have POSIX mmap() SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + AC_MSG_CHECKING(for mmap() using regular file shared memory support) + AC_TRY_RUN([ +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +int main() { + pid_t pid; + int status; + int fd; + char *shm; + char tmpname[4096]; + + sprintf(tmpname,"test.shm.%dXXXXXX", getpid()); + if (mktemp(tmpname) == NULL) { + return 1; + } + fd = open(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + return 2; + } + if (ftruncate(fd, 4096) < 0) { + close(fd); + unlink(tmpname); + return 3; + } + + shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (shm == MAP_FAILED) { + return 4; + } + unlink(tmpname); + close(fd); + + strcpy(shm, "hello"); + + pid = fork(); + if (pid < 0) { + return 5; + } else if (pid == 0) { + strcpy(shm, "bye"); + return 6; + } + if (wait(&status) != pid) { + return 7; + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) { + return 8; + } + if (strcmp(shm, "bye") != 0) { + return 9; + } + return 0; +} +],dnl + AC_DEFINE(HAVE_SHM_MMAP_FILE, 1, [Define if you have mmap() SHM support]) + msg=yes,msg=no,msg=no) + AC_MSG_RESULT([$msg]) + + PHP_NEW_EXTENSION(opcache, + ZendAccelerator.c \ + zend_accelerator_blacklist.c \ + zend_accelerator_debug.c \ + zend_accelerator_hash.c \ + zend_accelerator_module.c \ + zend_persist.c \ + zend_persist_calc.c \ + zend_shared_alloc.c \ + zend_accelerator_util_funcs.c \ + shared_alloc_shm.c \ + shared_alloc_mmap.c \ + shared_alloc_posix.c \ + Optimizer/zend_optimizer.c, + shared,,,,yes) + + PHP_ADD_BUILD_DIR([$ext_builddir/Optimizer], 1) +fi diff --git a/ext/opcache/config.w32 b/ext/opcache/config.w32 new file mode 100644 index 000000000..280ce228c --- /dev/null +++ b/ext/opcache/config.w32 @@ -0,0 +1,27 @@ +ARG_ENABLE("opcache", "whether to enable Zend OPcache support", "yes"); + +if (PHP_OPCACHE != "no") { + + PHP_PGI = "no"; // workaround + PHP_PGO = "no"; // workaround + + EXTENSION('opcache', "\ + ZendAccelerator.c \ + zend_accelerator_blacklist.c \ + zend_accelerator_debug.c \ + zend_accelerator_hash.c \ + zend_accelerator_module.c \ + zend_accelerator_util_funcs.c \ + zend_persist.c \ + zend_persist_calc.c \ + zend_shared_alloc.c \ + shared_alloc_win32.c", true); + + ADD_SOURCES(configure_module_dirname + "/Optimizer", "zend_optimizer.c", "opcache", "OptimizerObj"); + + + ADD_FLAG('CFLAGS_OPCACHE', "/I " + configure_module_dirname); + + ADD_FLAG('CFLAGS_OPCACHE', "/Dregexec=php_regexec /Dregerror=php_regerror /Dregfree=php_regfree /Dregcomp=php_regcomp /Iext/ereg/regex"); + +} diff --git a/ext/opcache/shared_alloc_mmap.c b/ext/opcache/shared_alloc_mmap.c new file mode 100644 index 000000000..12f00554a --- /dev/null +++ b/ext/opcache/shared_alloc_mmap.c @@ -0,0 +1,78 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "zend_shared_alloc.h" + +#ifdef USE_MMAP + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> + +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +# define MAP_ANONYMOUS MAP_ANON +#endif + +static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in) +{ + zend_shared_segment *shared_segment; + + *shared_segments_count = 1; + *shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment) + sizeof(void *)); + if (!*shared_segments_p) { + *error_in = "calloc"; + return ALLOC_FAILURE; + } + shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *)); + (*shared_segments_p)[0] = shared_segment; + + shared_segment->p = mmap(0, requested_size, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); + if (shared_segment->p == MAP_FAILED) { + *error_in = "mmap"; + return ALLOC_FAILURE; + } + + shared_segment->pos = 0; + shared_segment->size = requested_size; + + return ALLOC_SUCCESS; +} + +static int detach_segment(zend_shared_segment *shared_segment) +{ + munmap(shared_segment->p, shared_segment->size); + return 0; +} + +static size_t segment_type_size(void) +{ + return sizeof(zend_shared_segment); +} + +zend_shared_memory_handlers zend_alloc_mmap_handlers = { + create_segments, + detach_segment, + segment_type_size +}; + +#endif /* USE_MMAP */ diff --git a/ext/opcache/shared_alloc_posix.c b/ext/opcache/shared_alloc_posix.c new file mode 100644 index 000000000..f3377dec6 --- /dev/null +++ b/ext/opcache/shared_alloc_posix.c @@ -0,0 +1,98 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "zend_shared_alloc.h" + +#ifdef USE_SHM_OPEN + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> +#include <stdlib.h> + +typedef struct { + zend_shared_segment common; + int shm_fd; +} zend_shared_segment_posix; + +static int create_segments(size_t requested_size, zend_shared_segment_posix ***shared_segments_p, int *shared_segments_count, char **error_in) +{ + zend_shared_segment_posix *shared_segment; + char shared_segment_name[sizeof("/ZendAccelerator.") + 20]; + + *shared_segments_count = 1; + *shared_segments_p = (zend_shared_segment_posix **) calloc(1, sizeof(zend_shared_segment_posix) + sizeof(void *)); + if (!*shared_segments_p) { + *error_in = "calloc"; + return ALLOC_FAILURE; + } + shared_segment = (zend_shared_segment_posix *)((char *)(*shared_segments_p) + sizeof(void *)); + (*shared_segments_p)[0] = shared_segment; + + sprintf(shared_segment_name, "/ZendAccelerator.%d", getpid()); + shared_segment->shm_fd = shm_open(shared_segment_name, O_RDWR|O_CREAT|O_TRUNC, 0600); + if (shared_segment->shm_fd == -1) { + *error_in = "shm_open"; + return ALLOC_FAILURE; + } + + if (ftruncate(shared_segment->shm_fd, requested_size) != 0) { + *error_in = "ftruncate"; + shm_unlink(shared_segment_name); + return ALLOC_FAILURE; + } + + shared_segment->common.p = mmap(0, requested_size, PROT_READ | PROT_WRITE, MAP_SHARED, shared_segment->shm_fd, 0); + if (shared_segment->common.p == MAP_FAILED) { + *error_in = "mmap"; + shm_unlink(shared_segment_name); + return ALLOC_FAILURE; + } + shm_unlink(shared_segment_name); + + shared_segment->common.pos = 0; + shared_segment->common.size = requested_size; + + return ALLOC_SUCCESS; +} + +static int detach_segment(zend_shared_segment_posix *shared_segment) +{ + munmap(shared_segment->common.p, shared_segment->common.size); + close(shared_segment->shm_fd); + return 0; +} + +static size_t segment_type_size(void) +{ + return sizeof(zend_shared_segment_posix); +} + +zend_shared_memory_handlers zend_alloc_posix_handlers = { + (create_segments_t)create_segments, + (detach_segment_t)detach_segment, + segment_type_size +}; + +#endif /* USE_SHM_OPEN */ diff --git a/ext/opcache/shared_alloc_shm.c b/ext/opcache/shared_alloc_shm.c new file mode 100644 index 000000000..d2b842304 --- /dev/null +++ b/ext/opcache/shared_alloc_shm.c @@ -0,0 +1,145 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "zend_shared_alloc.h" + +#ifdef USE_SHM + +#if defined(__FreeBSD__) +# include <machine/param.h> +#endif +#include <sys/types.h> +#include <sys/shm.h> +#include <sys/ipc.h> +#include <dirent.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <sys/stat.h> +#include <fcntl.h> + +#ifndef MIN +# define MIN(x, y) ((x) > (y)? (y) : (x)) +#endif + +#define SEG_ALLOC_SIZE_MAX 32*1024*1024 +#define SEG_ALLOC_SIZE_MIN 2*1024*1024 + +typedef struct { + zend_shared_segment common; + int shm_id; +} zend_shared_segment_shm; + +static int create_segments(size_t requested_size, zend_shared_segment_shm ***shared_segments_p, int *shared_segments_count, char **error_in) +{ + int i; + unsigned int allocate_size = 0, remaining_bytes = requested_size, seg_allocate_size; + int first_segment_id = -1; + key_t first_segment_key = -1; + struct shmid_ds sds; + int shmget_flags; + zend_shared_segment_shm *shared_segments; + + seg_allocate_size = SEG_ALLOC_SIZE_MAX; + /* determine segment size we _really_ need: + * no more than to include requested_size + */ + while (requested_size * 2 <= seg_allocate_size && seg_allocate_size > SEG_ALLOC_SIZE_MIN) { + seg_allocate_size >>= 1; + } + + shmget_flags = IPC_CREAT|SHM_R|SHM_W|IPC_EXCL; + + /* try allocating this much, if not - try shrinking */ + while (seg_allocate_size >= SEG_ALLOC_SIZE_MIN) { + allocate_size = MIN(requested_size, seg_allocate_size); + first_segment_id = shmget(first_segment_key, allocate_size, shmget_flags); + if (first_segment_id != -1) { + break; + } + seg_allocate_size >>= 1; /* shrink the allocated block */ + } + + if (first_segment_id == -1) { + *error_in = "shmget"; + return ALLOC_FAILURE; + } + + *shared_segments_count = ((requested_size - 1) / seg_allocate_size) + 1; + *shared_segments_p = (zend_shared_segment_shm **) calloc(1, (*shared_segments_count) * sizeof(zend_shared_segment_shm) + sizeof(void *) * (*shared_segments_count)); + if (!*shared_segments_p) { + *error_in = "calloc"; + return ALLOC_FAILURE; + } + shared_segments = (zend_shared_segment_shm *)((char *)(*shared_segments_p) + sizeof(void *) * (*shared_segments_count)); + for (i = 0; i < *shared_segments_count; i++) { + (*shared_segments_p)[i] = shared_segments + i; + } + + remaining_bytes = requested_size; + for (i = 0; i < *shared_segments_count; i++) { + allocate_size = MIN(remaining_bytes, seg_allocate_size); + if (i != 0) { + shared_segments[i].shm_id = shmget(IPC_PRIVATE, allocate_size, shmget_flags); + } else { + shared_segments[i].shm_id = first_segment_id; + } + + if (shared_segments[i].shm_id == -1) { + return ALLOC_FAILURE; + } + + shared_segments[i].common.p = shmat(shared_segments[i].shm_id, NULL, 0); + if (((int) shared_segments[i].common.p) == -1) { + *error_in = "shmat"; + shmctl(shared_segments[i].shm_id, IPC_RMID, &sds); + return ALLOC_FAILURE; + } + shmctl(shared_segments[i].shm_id, IPC_RMID, &sds); + + shared_segments[i].common.pos = 0; + shared_segments[i].common.size = allocate_size; + remaining_bytes -= allocate_size; + } + return ALLOC_SUCCESS; +} + +static int detach_segment(zend_shared_segment_shm *shared_segment) +{ + shmdt(shared_segment->common.p); + return 0; +} + +static size_t segment_type_size(void) +{ + return sizeof(zend_shared_segment_shm); +} + +zend_shared_memory_handlers zend_alloc_shm_handlers = { + (create_segments_t)create_segments, + (detach_segment_t)detach_segment, + segment_type_size +}; + +#endif /* USE_SHM */ diff --git a/ext/opcache/shared_alloc_win32.c b/ext/opcache/shared_alloc_win32.c new file mode 100644 index 000000000..e32395284 --- /dev/null +++ b/ext/opcache/shared_alloc_win32.c @@ -0,0 +1,340 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "ZendAccelerator.h" +#include "zend_shared_alloc.h" +#include "zend_accelerator_util_funcs.h" +#include <winbase.h> +#include <process.h> +#include <LMCONS.H> + +#define ACCEL_FILEMAP_NAME "ZendOPcache.SharedMemoryArea" +#define ACCEL_MUTEX_NAME "ZendOPcache.SharedMemoryMutex" +#define ACCEL_FILEMAP_BASE_DEFAULT 0x01000000 +#define ACCEL_FILEMAP_BASE "ZendOPcache.MemoryBase" +#define ACCEL_EVENT_SOURCE "Zend OPcache" + +static HANDLE memfile = NULL, memory_mutex = NULL; +static void *mapping_base; + +#define MAX_MAP_RETRIES 25 + +static void zend_win_error_message(int type, char *msg, int err) +{ + LPVOID lpMsgBuf; + HANDLE h; + char *ev_msgs[2]; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + h = RegisterEventSource(NULL, TEXT(ACCEL_EVENT_SOURCE)); + ev_msgs[0] = msg; + ev_msgs[1] = lpMsgBuf; + ReportEvent(h, // event log handle + EVENTLOG_ERROR_TYPE, // event type + 0, // category zero + err, // event identifier + NULL, // no user security identifier + 2, // one substitution string + 0, // no data + ev_msgs, // pointer to string array + NULL); // pointer to data + DeregisterEventSource(h); + + LocalFree( lpMsgBuf ); + + zend_accel_error(type, msg); +} + +static char *create_name_with_username(char *name) +{ + static char newname[MAXPATHLEN + UNLEN + 4]; + char uname[UNLEN + 1]; + DWORD unsize = UNLEN; + + GetUserName(uname, &unsize); + snprintf(newname, sizeof(newname) - 1, "%s@%s", name, uname); + return newname; +} + +static char *get_mmap_base_file(void) +{ + static char windir[MAXPATHLEN+UNLEN + 3 + sizeof("\\\\@")]; + char uname[UNLEN + 1]; + DWORD unsize = UNLEN; + int l; + + GetTempPath(MAXPATHLEN, windir); + GetUserName(uname, &unsize); + l = strlen(windir); + snprintf(windir + l, sizeof(windir) - l - 1, "\\%s@%s", ACCEL_FILEMAP_BASE, uname); + return windir; +} + +void zend_shared_alloc_create_lock(void) +{ + memory_mutex = CreateMutex(NULL, FALSE, create_name_with_username(ACCEL_MUTEX_NAME)); + if (!memory_mutex) { + zend_accel_error(ACCEL_LOG_FATAL, "Cannot create mutex"); + return; + } + ReleaseMutex(memory_mutex); +} + +void zend_shared_alloc_lock_win32(void) +{ + DWORD waitRes = WaitForSingleObject(memory_mutex, INFINITE); + + if (waitRes == WAIT_FAILED) { + zend_accel_error(ACCEL_LOG_ERROR, "Cannot lock mutex"); + } +} + +void zend_shared_alloc_unlock_win32(void) +{ + ReleaseMutex(memory_mutex); +} + +static int zend_shared_alloc_reattach(size_t requested_size, char **error_in) +{ + int err; + void *wanted_mapping_base; + char *mmap_base_file = get_mmap_base_file(); + FILE *fp = fopen(mmap_base_file, "r"); + MEMORY_BASIC_INFORMATION info; + + err = GetLastError(); + if (!fp) { + zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open base address file", err); + *error_in="fopen"; + return ALLOC_FAILURE; + } + if (!fscanf(fp, "%p", &wanted_mapping_base)) { + err = GetLastError(); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read base address", err); + *error_in="read mapping base"; + fclose(fp); + return ALLOC_FAILURE; + } + fclose(fp); + + /* Check if the requested address space is free */ + if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 || + info.State != MEM_FREE || + info.RegionSize < requested_size) { + err = ERROR_INVALID_ADDRESS; + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err); + return ALLOC_FAILURE; + } + + mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base); + err = GetLastError(); + + if (mapping_base == NULL) { + if (err == ERROR_INVALID_ADDRESS) { + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err); + return ALLOC_FAILURE; + } + return ALLOC_FAIL_MAPPING; + } + smm_shared_globals = (zend_smm_shared_globals *) (((char *) mapping_base) + sizeof(zend_shared_memory_block_header)); + + return SUCCESSFULLY_REATTACHED; +} + +static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in) +{ + int err, ret; + zend_shared_segment *shared_segment; + int map_retries = 0; + void *default_mapping_base_set[] = { 0, 0 }; + void *vista_mapping_base_set[] = { (void *)0x20000000, (void *)0x21000000, (void *)0x30000000, (void *)0x31000000, (void *)0x50000000, 0 }; + void **wanted_mapping_base = default_mapping_base_set; + TSRMLS_FETCH(); + + zend_shared_alloc_lock_win32(); + /* Mapping retries: When Apache2 restarts, the parent process startup routine + can be called before the child process is killed. In this case, the map will fail + and we have to sleep some time (until the child releases the mapping object) and retry.*/ + do { + memfile = OpenFileMapping(FILE_MAP_WRITE, 0, create_name_with_username(ACCEL_FILEMAP_NAME)); + err = GetLastError(); + if (memfile == NULL) { + break; + } + + ret = zend_shared_alloc_reattach(requested_size, error_in); + err = GetLastError(); + if (ret == ALLOC_FAIL_MAPPING) { + /* Mapping failed, wait for mapping object to get freed and retry */ + CloseHandle(memfile); + memfile = NULL; + Sleep(1000 * (map_retries + 1)); + } else { + zend_shared_alloc_unlock_win32(); + return ret; + } + } while (++map_retries < MAX_MAP_RETRIES); + + if (map_retries == MAX_MAP_RETRIES) { + zend_shared_alloc_unlock_win32(); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open file mapping", err); + *error_in = "OpenFileMapping"; + return ALLOC_FAILURE; + } + + /* creating segment here */ + *shared_segments_count = 1; + *shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *)); + if (!*shared_segments_p) { + zend_shared_alloc_unlock_win32(); + zend_win_error_message(ACCEL_LOG_FATAL, "calloc() failed", GetLastError()); + *error_in = "calloc"; + return ALLOC_FAILURE; + } + shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *)); + (*shared_segments_p)[0] = shared_segment; + + memfile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, requested_size, + create_name_with_username(ACCEL_FILEMAP_NAME)); + err = GetLastError(); + if (memfile == NULL) { + zend_shared_alloc_unlock_win32(); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create file mapping", err); + *error_in = "CreateFileMapping"; + return ALLOC_FAILURE; + } + + /* Starting from windows Vista, heap randomization occurs which might cause our mapping base to + be taken (fail to map). So under Vista, we try to map into a hard coded predefined addresses + in high memory. */ + if (!ZCG(accel_directives).mmap_base || !*ZCG(accel_directives).mmap_base) { + do { + OSVERSIONINFOEX osvi; + SYSTEM_INFO si; + + ZeroMemory(&si, sizeof(SYSTEM_INFO)); + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + if (! GetVersionEx ((OSVERSIONINFO *) &osvi)) { + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx((OSVERSIONINFO *)&osvi)) { + break; + } + } + + GetSystemInfo(&si); + + /* Are we running Vista ? */ + if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion == 6) { + /* Assert that platform is 32 bit (for 64 bit we need to test a different set */ + if (si.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL) { + DebugBreak(); + } + + wanted_mapping_base = vista_mapping_base_set; + } + } while (0); + } else { + char *s = ZCG(accel_directives).mmap_base; + + /* skip leading 0x, %p assumes hexdeciaml format anyway */ + if (*s == '0' && *(s + 1) == 'x') { + s += 2; + } + if (sscanf(s, "%p", &default_mapping_base_set[0]) != 1) { + zend_shared_alloc_unlock_win32(); + zend_win_error_message(ACCEL_LOG_FATAL, "Bad mapping address specified in opcache.mmap_base", err); + return ALLOC_FAILURE; + } + } + + do { + shared_segment->p = mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, *wanted_mapping_base); + if (*wanted_mapping_base == NULL) { /* Auto address (NULL) is the last option on the array */ + break; + } + wanted_mapping_base++; + } while (!mapping_base); + + err = GetLastError(); + if (mapping_base == NULL) { + zend_shared_alloc_unlock_win32(); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create view for file mapping", err); + *error_in = "MapViewOfFile"; + return ALLOC_FAILURE; + } else { + char *mmap_base_file = get_mmap_base_file(); + FILE *fp = fopen(mmap_base_file, "w"); + err = GetLastError(); + if (!fp) { + zend_shared_alloc_unlock_win32(); + zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err); + zend_win_error_message(ACCEL_LOG_FATAL, "Unable to write base address", err); + return ALLOC_FAILURE; + } + fprintf(fp, "%p\n", mapping_base); + fclose(fp); + } + + shared_segment->pos = 0; + shared_segment->size = requested_size; + + zend_shared_alloc_unlock_win32(); + + return ALLOC_SUCCESS; +} + +static int detach_segment(zend_shared_segment *shared_segment) +{ + zend_shared_alloc_lock_win32(); + if (mapping_base) { + UnmapViewOfFile(mapping_base); + } + CloseHandle(memfile); + zend_shared_alloc_unlock_win32(); + CloseHandle(memory_mutex); + return 0; +} + +static size_t segment_type_size(void) +{ + return sizeof(zend_shared_segment); +} + +zend_shared_memory_handlers zend_alloc_win32_handlers = { + create_segments, + detach_segment, + segment_type_size +}; diff --git a/ext/opcache/tests/001_cli.phpt b/ext/opcache/tests/001_cli.phpt new file mode 100644 index 000000000..c51db23f5 --- /dev/null +++ b/ext/opcache/tests/001_cli.phpt @@ -0,0 +1,19 @@ +--TEST-- +001: O+ works in CLI +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php +$config = opcache_get_configuration(); +$status = opcache_get_status(); +var_dump($config["directives"]["opcache.enable"]); +var_dump($config["directives"]["opcache.enable_cli"]); +var_dump($status["opcache_enabled"]); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/ext/opcache/tests/bug64353.phpt b/ext/opcache/tests/bug64353.phpt new file mode 100644 index 000000000..b1f0c6e71 --- /dev/null +++ b/ext/opcache/tests/bug64353.phpt @@ -0,0 +1,29 @@ +--TEST-- +Bug #64353 (Built-in classes can be unavailable with dynamic includes and OPcache) +--INI-- +allow_url_include=1 +opcache.enable=1 +opcache.enable_cli=1 +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php +class BugLoader extends php_user_filter { + public function filter($in, $out, &$consumed, $closing) { + if (!class_exists("Test")) { + eval("class Test extends ArrayObject {}"); + } + while ($bucket = stream_bucket_make_writeable($in)) { + $consumed += $bucket->datalen; + stream_bucket_append($out, $bucket); + } + return PSFS_PASS_ON; + } +} + +stream_filter_register('bug.test', 'BugLoader'); +include "php://filter/read=bug.test/resource=data://text/plain,<?php\n"; +echo "OK\n"; +?> +--EXPECT-- +OK diff --git a/ext/opcache/tests/issue0057.phpt b/ext/opcache/tests/issue0057.phpt new file mode 100644 index 000000000..49e9156f1 --- /dev/null +++ b/ext/opcache/tests/issue0057.phpt @@ -0,0 +1,38 @@ +--TEST-- +ISSUE #57 (segfaults in drupal7) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php + +class ZException extends Exception { +} + +function dummy($query) { + try { + switch ($query) { + case 1; + break; + case 2; + break; + default: + throw new Exception('exception'); + } + } catch (ZException $e) { + return NULL; + } +} + +try { + dummy(0); +} catch (Exception $e) { + echo $e->getMessage(); +} + +?> +--EXPECT-- +exception diff --git a/ext/opcache/tests/skipif.inc b/ext/opcache/tests/skipif.inc new file mode 100644 index 000000000..c5a818103 --- /dev/null +++ b/ext/opcache/tests/skipif.inc @@ -0,0 +1,3 @@ +<?php + if (!extension_loaded('Zend OPcache')) die('skip Zend OPcache extension not available'); +?> diff --git a/ext/opcache/zend_accelerator_blacklist.c b/ext/opcache/zend_accelerator_blacklist.c new file mode 100644 index 000000000..0ccd62e34 --- /dev/null +++ b/ext/opcache/zend_accelerator_blacklist.c @@ -0,0 +1,261 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "main/php.h" +#include "main/fopen_wrappers.h" +#include "ZendAccelerator.h" +#include "zend_accelerator_blacklist.h" + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO +# include "ext/ereg/php_regex.h" +#else +# include "main/php_regex.h" +#endif + +#ifdef ZEND_WIN32 +# define REGEX_MODE (REG_EXTENDED|REG_NOSUB|REG_ICASE) +#else +# define REGEX_MODE (REG_EXTENDED|REG_NOSUB) +#endif + +#define ZEND_BLACKLIST_BLOCK_SIZE 32 + +struct _zend_regexp_list { + regex_t comp_regex; + zend_regexp_list *next; +}; + +zend_blacklist accel_blacklist; + +void zend_accel_blacklist_init(zend_blacklist *blacklist) +{ + blacklist->pos = 0; + blacklist->size = ZEND_BLACKLIST_BLOCK_SIZE; + + if (blacklist->entries != NULL) { + zend_accel_blacklist_shutdown(blacklist); + } + + blacklist->entries = (zend_blacklist_entry *) calloc(sizeof(zend_blacklist_entry), blacklist->size); + if (!blacklist->entries) { + zend_accel_error(ACCEL_LOG_FATAL, "Blacklist initialization: no memory\n"); + return; + } + blacklist->regexp_list = NULL; +} + +static void blacklist_report_regexp_error(regex_t *comp_regex, int reg_err) +{ + char *errbuf; + int errsize = regerror(reg_err, comp_regex, NULL, 0); + errbuf = malloc(errsize); + if (!errbuf) { + zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation: no memory\n"); + return; + } + regerror(reg_err, comp_regex, errbuf, errsize); + zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation: %s\n", errbuf); + free(errbuf); +} + +static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist) +{ + char *regexp; + int i, j, clen, reg_err, end = 0, rlen = 6; + zend_regexp_list **regexp_list_it, *it; + + if (blacklist->pos == 0) { + /* we have no blacklist to talk about */ + return; + } + + regexp_list_it = &(blacklist->regexp_list); + for (i = 0; i < blacklist->pos; i++) { + rlen += blacklist->entries[i].path_length * 2 + 2; + + /* don't create a regexp buffer bigger than 12K)*/ + if ((i + 1 == blacklist->pos) || ((rlen + blacklist->entries[i + 1].path_length * 2 + 2) > (12 * 1024))) { + regexp = (char *)malloc(rlen); + if (!regexp) { + zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n"); + return; + } + regexp[0] = '^'; + regexp[1] = '('; + + clen = 2; + for (j = end; j <= i; j++) { + + int c; + if (j != end) { + regexp[clen++] = '|'; + } + /* copy mangled filename */ + for (c = 0; c < blacklist->entries[j].path_length; c++) { + if (strchr("^.[]$()|*+?{}\\", blacklist->entries[j].path[c])) { + regexp[clen++] = '\\'; + } + regexp[clen++] = blacklist->entries[j].path[c]; + } + } + regexp[clen++] = ')'; + regexp[clen] = '\0'; + + it = (zend_regexp_list*)malloc(sizeof(zend_regexp_list)); + if (!it) { + zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n"); + return; + } + it->next = NULL; + + if ((reg_err = regcomp(&it->comp_regex, regexp, REGEX_MODE)) != 0) { + blacklist_report_regexp_error(&it->comp_regex, reg_err); + } + /* prepare for the next iteration */ + free(regexp); + end = i + 1; + rlen = 6; + *regexp_list_it = it; + regexp_list_it = &it->next; + } + } +} + +void zend_accel_blacklist_shutdown(zend_blacklist *blacklist) +{ + zend_blacklist_entry *p = blacklist->entries, *end = blacklist->entries + blacklist->pos; + + while (p<end) { + free(p->path); + p++; + } + free(blacklist->entries); + blacklist->entries = NULL; + if (blacklist->regexp_list) { + zend_regexp_list *temp, *it = blacklist->regexp_list; + while (it) { + regfree(&it->comp_regex); + temp = it; + it = it->next; + free(temp); + } + } +} + +static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist) +{ + if (blacklist->pos == blacklist->size) { + blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE; + blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size); + } +} + +void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename) +{ + char buf[MAXPATHLEN + 1], real_path[MAXPATHLEN + 1]; + FILE *fp; + int path_length; + TSRMLS_FETCH(); + + if ((fp = fopen(filename, "r")) == NULL) { + zend_accel_error(ACCEL_LOG_WARNING, "Cannot load blacklist file: %s\n", filename); + return; + } + + zend_accel_error(ACCEL_LOG_DEBUG,"Loading blacklist file: '%s'", filename); + + memset(buf, 0, sizeof(buf)); + memset(real_path, 0, sizeof(real_path)); + + while (fgets(buf, MAXPATHLEN, fp) != NULL) { + char *path_dup, *pbuf; + path_length = strlen(buf); + if (path_length > 0 && buf[path_length - 1] == '\n') { + buf[--path_length] = 0; + if (path_length > 0 && buf[path_length - 1] == '\r') { + buf[--path_length] = 0; + } + } + + /* Strip ctrl-m prefix */ + pbuf = &buf[0]; + while (*pbuf == '\r') { + *pbuf++ = 0; + path_length--; + } + + /* strip \" */ + if (pbuf[0] == '\"' && pbuf[path_length - 1]== '\"') { + *pbuf++ = 0; + path_length -= 2; + } + + if (path_length == 0) { + continue; + } + + path_dup = zend_strndup(pbuf, path_length); + expand_filepath(path_dup, real_path TSRMLS_CC); + path_length = strlen(real_path); + + free(path_dup); + + zend_accel_blacklist_allocate(blacklist); + blacklist->entries[blacklist->pos].path_length = path_length; + blacklist->entries[blacklist->pos].path = (char *)malloc(path_length + 1); + if (!blacklist->entries[blacklist->pos].path) { + zend_accel_error(ACCEL_LOG_ERROR, "malloc() failed\n"); + return; + } + blacklist->entries[blacklist->pos].id = blacklist->pos; + memcpy(blacklist->entries[blacklist->pos].path, real_path, path_length + 1); + blacklist->pos++; + } + fclose(fp); + zend_accel_blacklist_update_regexp(blacklist); +} + +zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path) +{ + int ret = 0; + zend_regexp_list *regexp_list_it = blacklist->regexp_list; + + if (regexp_list_it == NULL) { + return 0; + } + while (regexp_list_it != NULL) { + if (regexec(&(regexp_list_it->comp_regex), verify_path, 0, NULL, 0) == 0) { + ret = 1; + break; + } + regexp_list_it = regexp_list_it->next; + } + return ret; +} + +void zend_accel_blacklist_apply(zend_blacklist *blacklist, apply_func_arg_t func, void *argument TSRMLS_DC) +{ + int i; + + for (i = 0; i < blacklist->pos; i++) { + func(&blacklist->entries[i], argument TSRMLS_CC); + } +} diff --git a/ext/opcache/zend_accelerator_blacklist.h b/ext/opcache/zend_accelerator_blacklist.h new file mode 100644 index 000000000..626b8d2c4 --- /dev/null +++ b/ext/opcache/zend_accelerator_blacklist.h @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_BLACKLIST_H +#define ZEND_ACCELERATOR_BLACKLIST_H + +typedef struct _zend_regexp_list zend_regexp_list; + +typedef struct _zend_blacklist_entry { + char *path; + int path_length; + int id; +} zend_blacklist_entry; + +typedef struct _zend_blacklist { + zend_blacklist_entry *entries; + int size; + int pos; + zend_regexp_list *regexp_list; +} zend_blacklist; + +extern zend_blacklist accel_blacklist; + +void zend_accel_blacklist_init(zend_blacklist *blacklist); +void zend_accel_blacklist_shutdown(zend_blacklist *blacklist); + +void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename); +zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path); +void zend_accel_blacklist_apply(zend_blacklist *blacklist, apply_func_arg_t func, void *argument TSRMLS_DC); + +#endif /* ZEND_ACCELERATOR_BLACKLIST_H */ diff --git a/ext/opcache/zend_accelerator_debug.c b/ext/opcache/zend_accelerator_debug.c new file mode 100644 index 000000000..93349e3d1 --- /dev/null +++ b/ext/opcache/zend_accelerator_debug.c @@ -0,0 +1,99 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <time.h> +#ifdef ZEND_WIN32 +# include <process.h> +#endif +#include "ZendAccelerator.h" + +void zend_accel_error(int type, const char *format, ...) +{ + va_list args; + time_t timestamp; + char *time_string; + FILE * fLog = NULL; + TSRMLS_FETCH(); + + if (type > ZCG(accel_directives).log_verbosity_level) { + return; + } + + timestamp = time(NULL); + time_string = asctime(localtime(×tamp)); + time_string[24] = 0; + + if (!ZCG(accel_directives).error_log || + !*ZCG(accel_directives).error_log || + strcmp(ZCG(accel_directives).error_log, "stderr") == 0) { + + fLog = stderr; + } else { + fLog = fopen(ZCG(accel_directives).error_log, "a+"); + if (!fLog) { + fLog = stderr; + } + } + +#ifdef ZTS + fprintf(fLog, "%s (%lu): ", time_string, (unsigned long)tsrm_thread_id()); +#else + fprintf(fLog, "%s (%d): ", time_string, getpid()); +#endif + + switch (type) { + case ACCEL_LOG_FATAL: + fprintf(fLog, "Fatal Error "); + break; + case ACCEL_LOG_ERROR: + fprintf(fLog, "Error "); + break; + case ACCEL_LOG_WARNING: + fprintf(fLog, "Warning "); + break; + case ACCEL_LOG_INFO: + fprintf(fLog, "Message "); + break; + case ACCEL_LOG_DEBUG: + fprintf(fLog, "Debug "); + break; + } + + va_start(args, format); + vfprintf(fLog, format, args); + va_end(args); + fprintf(fLog, "\n"); + switch (type) { + case ACCEL_LOG_ERROR: + zend_bailout(); + break; + case ACCEL_LOG_FATAL: + exit(-2); + break; + } + fflush(fLog); + if (fLog != stderr) { + fclose(fLog); + } +} diff --git a/ext/opcache/zend_accelerator_debug.h b/ext/opcache/zend_accelerator_debug.h new file mode 100644 index 000000000..2ff88e21d --- /dev/null +++ b/ext/opcache/zend_accelerator_debug.h @@ -0,0 +1,33 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_DEBUG_H +#define ZEND_ACCELERATOR_DEBUG_H + +#define ACCEL_LOG_FATAL 0 +#define ACCEL_LOG_ERROR 1 +#define ACCEL_LOG_WARNING 2 +#define ACCEL_LOG_INFO 3 +#define ACCEL_LOG_DEBUG 4 + +void zend_accel_error(int type, const char *format, ...); + +#endif /* _ZEND_ACCELERATOR_DEBUG_H */ diff --git a/ext/opcache/zend_accelerator_hash.c b/ext/opcache/zend_accelerator_hash.c new file mode 100644 index 000000000..afd227c5d --- /dev/null +++ b/ext/opcache/zend_accelerator_hash.c @@ -0,0 +1,224 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "ZendAccelerator.h" +#include "zend_accelerator_hash.h" +#include "zend_hash.h" +#include "zend_shared_alloc.h" + +/* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */ +static uint prime_numbers[] = + {5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 }; +static uint num_prime_numbers = sizeof(prime_numbers) / sizeof(uint); + +void zend_accel_hash_clean(zend_accel_hash *accel_hash) +{ + accel_hash->num_entries = 0; + accel_hash->num_direct_entries = 0; + memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); +} + +void zend_accel_hash_init(zend_accel_hash *accel_hash, zend_uint hash_size) +{ + uint i; + + for (i=0; i<num_prime_numbers; i++) { + if (hash_size <= prime_numbers[i]) { + hash_size = prime_numbers[i]; + break; + } + } + + accel_hash->num_entries = 0; + accel_hash->num_direct_entries = 0; + accel_hash->max_num_entries = hash_size; + + /* set up hash pointers table */ + accel_hash->hash_table = zend_shared_alloc(sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); + if (!accel_hash->hash_table) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + return; + } + + /* set up hash values table */ + accel_hash->hash_entries = zend_shared_alloc(sizeof(zend_accel_hash_entry)*accel_hash->max_num_entries); + if (!accel_hash->hash_entries) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + return; + } + memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries); +} + +/* Returns NULL if hash is full + * Returns pointer the actual hash entry on success + * key needs to be already allocated as it is not copied + */ +zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, char *key, zend_uint key_length, zend_bool indirect, void *data) +{ + zend_ulong hash_value; + zend_ulong index; + zend_accel_hash_entry *entry; + zend_accel_hash_entry *indirect_bucket = NULL; + + if (indirect) { + indirect_bucket = (zend_accel_hash_entry*)data; + while (indirect_bucket->indirect) { + indirect_bucket = (zend_accel_hash_entry*)indirect_bucket->data; + } + } + + hash_value = zend_inline_hash_func(key, key_length); + index = hash_value % accel_hash->max_num_entries; + + /* try to see if the element already exists in the hash */ + entry = accel_hash->hash_table[index]; + while (entry) { + if (entry->hash_value == hash_value + && entry->key_length == key_length + && !memcmp(entry->key, key, key_length)) { + + if (entry->indirect) { + if (indirect_bucket) { + entry->data = indirect_bucket; + } else { + ((zend_accel_hash_entry*)entry->data)->data = data; + } + } else { + if (indirect_bucket) { + accel_hash->num_direct_entries--; + entry->data = indirect_bucket; + entry->indirect = 1; + } else { + entry->data = data; + } + } + return entry; + } + entry = entry->next; + } + + /* Does not exist, add a new entry */ + if (accel_hash->num_entries == accel_hash->max_num_entries) { + return NULL; + } + + entry = &accel_hash->hash_entries[accel_hash->num_entries++]; + if (indirect) { + entry->data = indirect_bucket; + entry->indirect = 1; + } else { + accel_hash->num_direct_entries++; + entry->data = data; + entry->indirect = 0; + } + entry->hash_value = hash_value; + entry->key = key; + entry->key_length = key_length; + entry->next = accel_hash->hash_table[index]; + accel_hash->hash_table[index] = entry; + return entry; +} + +/* Returns the data associated with key on success + * Returns NULL if data doesn't exist + */ +void* zend_accel_hash_find(zend_accel_hash *accel_hash, char *key, zend_uint key_length) +{ + zend_ulong hash_value; + zend_ulong index; + zend_accel_hash_entry *entry; + + hash_value = zend_inline_hash_func(key, key_length); + index = hash_value % accel_hash->max_num_entries; + + entry = accel_hash->hash_table[index]; + while (entry) { + if (entry->hash_value == hash_value + && entry->key_length == key_length + && !memcmp(entry->key, key, key_length)) { + if (entry->indirect) { + return ((zend_accel_hash_entry *) entry->data)->data; + } else { + return entry->data; + } + } + entry = entry->next; + } + return NULL; +} + +/* Returns the hash entry associated with key on success + * Returns NULL if it doesn't exist + */ +zend_accel_hash_entry* zend_accel_hash_find_entry(zend_accel_hash *accel_hash, char *key, zend_uint key_length) +{ + zend_ulong hash_value; + zend_ulong index; + zend_accel_hash_entry *entry; + + hash_value = zend_inline_hash_func(key, key_length); + index = hash_value % accel_hash->max_num_entries; + + entry = accel_hash->hash_table[index]; + while (entry) { + if (entry->hash_value == hash_value + && entry->key_length == key_length + && !memcmp(entry->key, key, key_length)) { + if (entry->indirect) { + return (zend_accel_hash_entry *) entry->data; + } else { + return entry; + } + } + entry = entry->next; + } + return NULL; +} + +int zend_accel_hash_unlink(zend_accel_hash *accel_hash, char *key, zend_uint key_length) +{ + zend_ulong hash_value; + zend_ulong index; + zend_accel_hash_entry *entry, *last_entry=NULL; + + hash_value = zend_inline_hash_func(key, key_length); + index = hash_value % accel_hash->max_num_entries; + + entry = accel_hash->hash_table[index]; + while (entry) { + if (entry->hash_value == hash_value + && entry->key_length == key_length + && !memcmp(entry->key, key, key_length)) { + if (!entry->indirect) { + accel_hash->num_direct_entries--; + } + if (last_entry) { + last_entry->next = entry->next; + } else { + accel_hash->hash_table[index] = entry->next; + } + return SUCCESS; + } + last_entry = entry; + entry = entry->next; + } + return FAILURE; +} diff --git a/ext/opcache/zend_accelerator_hash.h b/ext/opcache/zend_accelerator_hash.h new file mode 100644 index 000000000..fdfddb406 --- /dev/null +++ b/ext/opcache/zend_accelerator_hash.h @@ -0,0 +1,98 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_HASH_H +#define ZEND_ACCELERATOR_HASH_H + +#include "zend.h" + +/* + zend_accel_hash - is a hash table allocated in shared memory and + distributed across simultaneously running processes. The hash tables have + fixed sizen selected during construction by zend_accel_hash_init(). All the + hash entries are preallocated in the 'hash_entries' array. 'num_entries' is + initialized by zero and grows when new data is added. + zend_accel_hash_update() just takes the next entry from 'hash_entries' + array and puts it into appropriate place of 'hash_table'. + Hash collisions are resolved by separate chaining with linked lists, + however, entries are still taken from the same 'hash_entries' array. + 'key' and 'data' passed to zend_accel_hash_update() must be already + allocated in shared memory. Few keys may be resolved to the same data. + using 'indirect' entries, that point to other entries ('data' is actually + a pointer to another zend_accel_hash_entry). + zend_accel_hash_update() requires exclusive lock, however, + zend_accel_hash_find() does not. +*/ + +typedef struct _zend_accel_hash_entry zend_accel_hash_entry; + +struct _zend_accel_hash_entry { + zend_ulong hash_value; + char *key; + zend_uint key_length; + zend_accel_hash_entry *next; + void *data; + zend_bool indirect; +}; + +typedef struct _zend_accel_hash { + zend_accel_hash_entry **hash_table; + zend_accel_hash_entry *hash_entries; + zend_uint num_entries; + zend_uint max_num_entries; + zend_uint num_direct_entries; +} zend_accel_hash; + +void zend_accel_hash_init(zend_accel_hash *accel_hash, zend_uint hash_size); +void zend_accel_hash_clean(zend_accel_hash *accel_hash); + +zend_accel_hash_entry* zend_accel_hash_update( + zend_accel_hash *accel_hash, + char *key, + zend_uint key_length, + zend_bool indirect, + void *data); + +void* zend_accel_hash_find( + zend_accel_hash *accel_hash, + char *key, + zend_uint key_length); + +zend_accel_hash_entry* zend_accel_hash_find_entry( + zend_accel_hash *accel_hash, + char *key, + zend_uint key_length); + +int zend_accel_hash_unlink( + zend_accel_hash *accel_hash, + char *key, + zend_uint key_length); + +static inline zend_bool zend_accel_hash_is_full(zend_accel_hash *accel_hash) +{ + if (accel_hash->num_entries == accel_hash->max_num_entries) { + return 1; + } else { + return 0; + } +} + +#endif /* ZEND_ACCELERATOR_HASH_H */ diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c new file mode 100644 index 000000000..778fee54b --- /dev/null +++ b/ext/opcache/zend_accelerator_module.c @@ -0,0 +1,618 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include <time.h> + +#include "php.h" +#include "ZendAccelerator.h" +#include "zend_API.h" +#include "zend_shared_alloc.h" +#include "zend_accelerator_blacklist.h" +#include "php_ini.h" +#include "SAPI.h" +#include "TSRM/tsrm_virtual_cwd.h" +#include "ext/standard/info.h" +#include "ext/standard/php_filestat.h" + +#define STRING_NOT_NULL(s) (NULL == (s)?"":s) +#define MIN_ACCEL_FILES 200 +#define MAX_ACCEL_FILES 100000 +#define TOKENTOSTR(X) #X + +static void (*orig_file_exists)(INTERNAL_FUNCTION_PARAMETERS) = NULL; +static void (*orig_is_file)(INTERNAL_FUNCTION_PARAMETERS) = NULL; +static void (*orig_is_readable)(INTERNAL_FUNCTION_PARAMETERS) = NULL; + +/* User functions */ +static ZEND_FUNCTION(opcache_reset); + +/* Private functions */ +static ZEND_FUNCTION(opcache_get_status); +static ZEND_FUNCTION(opcache_get_configuration); + +static zend_function_entry accel_functions[] = { + /* User functions */ + ZEND_FE(opcache_reset, NULL) + /* Private functions */ + ZEND_FE(opcache_get_configuration, NULL) + ZEND_FE(opcache_get_status, NULL) + { NULL, NULL, NULL, 0, 0 } +}; + +static ZEND_INI_MH(OnUpdateMemoryConsumption) +{ + long *p; + long memsize; +#ifndef ZTS + char *base = (char *) mh_arg2; +#else + char *base = (char *) ts_resource(*((int *) mh_arg2)); +#endif + + /* keep the compiler happy */ + (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage; + + p = (long *) (base + (size_t)mh_arg1); + memsize = atoi(new_value); + /* sanity check we must use at least 8 MB */ + if (memsize < 8) { + const char *new_new_value = "8"; + zend_ini_entry *ini_entry; + + memsize = 8; + zend_accel_error(ACCEL_LOG_WARNING, "opcache.memory_consumption is set below the required 8MB.\n"); + zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal 8MB configuration.\n"); + + if (zend_hash_find(EG(ini_directives), + "opcache.memory_consumption", + sizeof("opcache.memory_consumption"), + (void *) &ini_entry) == FAILURE) { + return FAILURE; + } + + ini_entry->value = strdup(new_new_value); + ini_entry->value_length = strlen(new_new_value); + } + *p = memsize * (1024 * 1024); + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateMaxAcceleratedFiles) +{ + long *p; + long size; +#ifndef ZTS + char *base = (char *) mh_arg2; +#else + char *base = (char *) ts_resource(*((int *) mh_arg2)); +#endif + + /* keep the compiler happy */ + (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage; + + p = (long *) (base + (size_t)mh_arg1); + size = atoi(new_value); + /* sanity check we must use a value between MIN_ACCEL_FILES and MAX_ACCEL_FILES */ + + if (size < MIN_ACCEL_FILES || size > MAX_ACCEL_FILES) { + const char *new_new_value; + zend_ini_entry *ini_entry; + + if (size < MIN_ACCEL_FILES) { + size = MIN_ACCEL_FILES; + new_new_value = TOKENTOSTR(MIN_ACCEL_FILES); + zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_accelerated_files is set below the required minimum (%d).\n", MIN_ACCEL_FILES); + zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal configuration.\n"); + } + if (size > MAX_ACCEL_FILES) { + size = MAX_ACCEL_FILES; + new_new_value = TOKENTOSTR(MAX_ACCEL_FILES); + zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_accelerated_files is set above the limit (%d).\n", MAX_ACCEL_FILES); + zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the maximal configuration.\n"); + } + if (zend_hash_find(EG(ini_directives), + "opcache.max_accelerated_files", + sizeof("opcache.max_accelerated_files"), + (void *) &ini_entry) == FAILURE) { + return FAILURE; + } + ini_entry->value = strdup(new_new_value); + ini_entry->value_length = strlen(new_new_value); + } + *p = size; + return SUCCESS; +} + +static ZEND_INI_MH(OnUpdateMaxWastedPercentage) +{ + double *p; + long percentage; +#ifndef ZTS + char *base = (char *) mh_arg2; +#else + char *base = (char *) ts_resource(*((int *) mh_arg2)); +#endif + + /* keep the compiler happy */ + (void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage; + + p = (double *) (base + (size_t)mh_arg1); + percentage = atoi(new_value); + + if (percentage <= 0 || percentage > 50) { + const char *new_new_value = "5"; + zend_ini_entry *ini_entry; + + percentage = 5; + zend_accel_error(ACCEL_LOG_WARNING, "opcache.max_wasted_percentage must be set between 1 and 50.\n"); + zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use 5%.\n"); + if (zend_hash_find(EG(ini_directives), + "opcache.max_wasted_percentage", + sizeof("opcache.max_wasted_percentage"), + (void *) &ini_entry) == FAILURE) { + return FAILURE; + } + ini_entry->value = strdup(new_new_value); + ini_entry->value_length = strlen(new_new_value); + } + *p = (double)percentage / 100.0; + return SUCCESS; +} + +ZEND_INI_BEGIN() + STD_PHP_INI_BOOLEAN("opcache.enable" , "1", PHP_INI_SYSTEM, OnUpdateBool, enabled , zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.use_cwd" , "1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.use_cwd , zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.validate_timestamps", "1", PHP_INI_ALL , OnUpdateBool, accel_directives.validate_timestamps, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.inherited_hack" , "1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.inherited_hack , zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.dups_fix" , "0", PHP_INI_ALL , OnUpdateBool, accel_directives.ignore_dups , zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.revalidate_path" , "0", PHP_INI_ALL , OnUpdateBool, accel_directives.revalidate_path , zend_accel_globals, accel_globals) + + STD_PHP_INI_ENTRY("opcache.log_verbosity_level" , "1" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.log_verbosity_level, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.memory_consumption" , "64" , PHP_INI_SYSTEM, OnUpdateMemoryConsumption, accel_directives.memory_consumption, zend_accel_globals, accel_globals) +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + STD_PHP_INI_ENTRY("opcache.interned_strings_buffer", "4" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.interned_strings_buffer, zend_accel_globals, accel_globals) +#endif + STD_PHP_INI_ENTRY("opcache.max_accelerated_files" , "2000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles, accel_directives.max_accelerated_files, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.max_wasted_percentage" , "5" , PHP_INI_SYSTEM, OnUpdateMaxWastedPercentage, accel_directives.max_wasted_percentage, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.consistency_checks" , "0" , PHP_INI_ALL , OnUpdateLong, accel_directives.consistency_checks, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.force_restart_timeout" , "180" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.force_restart_timeout, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.revalidate_freq" , "2" , PHP_INI_ALL , OnUpdateLong, accel_directives.revalidate_freq, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.preferred_memory_model", "" , PHP_INI_SYSTEM, OnUpdateStringUnempty, accel_directives.memory_model, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.blacklist_filename" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.user_blacklist_filename, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.max_file_size" , "0" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.max_file_size, zend_accel_globals, accel_globals) + + STD_PHP_INI_ENTRY("opcache.protect_memory" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.protect_memory, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.save_comments" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.save_comments, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.load_comments" , "1" , PHP_INI_ALL, OnUpdateBool, accel_directives.load_comments, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.fast_shutdown" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.fast_shutdown, zend_accel_globals, accel_globals) + + STD_PHP_INI_ENTRY("opcache.optimization_level" , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.enable_file_override" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_override_enabled, zend_accel_globals, accel_globals) + STD_PHP_INI_BOOLEAN("opcache.enable_cli" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.enable_cli, zend_accel_globals, accel_globals) + STD_PHP_INI_ENTRY("opcache.error_log" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.error_log, zend_accel_globals, accel_globals) + +#ifdef ZEND_WIN32 + STD_PHP_INI_ENTRY("opcache.mmap_base", NULL, PHP_INI_SYSTEM, OnUpdateString, accel_directives.mmap_base, zend_accel_globals, accel_globals) +#endif +ZEND_INI_END() + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + +#undef EX +#define EX(element) execute_data->element +#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset)) + +static int ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zend_class_entry **pce, **pce_orig; + + if (zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op2.u.constant), Z_STRLEN(EX(opline)->op2.u.constant) + 1, (void **)&pce) == FAILURE || + (zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op1.u.constant), Z_STRLEN(EX(opline)->op1.u.constant), (void**)&pce_orig) == SUCCESS && + *pce != *pce_orig)) { + do_bind_inherited_class(EX(opline), EG(class_table), EX_T(EX(opline)->extended_value).class_entry, 0 TSRMLS_CC); + } + EX(opline)++; + return ZEND_USER_OPCODE_CONTINUE; +} +#endif + +static int filename_is_in_cache(char *filename, int filename_len TSRMLS_DC) +{ + char *key; + int key_length; + zend_file_handle handle = {0}; + zend_persistent_script *persistent_script; + + handle.filename = filename; + handle.type = ZEND_HANDLE_FILENAME; + + if (IS_ABSOLUTE_PATH(filename, filename_len)) { + persistent_script = zend_accel_hash_find(&ZCSG(hash), filename, filename_len + 1); + if (persistent_script) { + return !persistent_script->corrupted; + } + } + + if ((key = accel_make_persistent_key_ex(&handle, filename_len, &key_length TSRMLS_CC)) != NULL) { + persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length + 1); + return persistent_script && !persistent_script->corrupted; + } + + return 0; +} + +static int accel_file_in_cache(INTERNAL_FUNCTION_PARAMETERS) +{ + zval **zfilename; + + if (ZEND_NUM_ARGS() != 1 || + zend_get_parameters_array_ex(1, &zfilename) == FAILURE || + Z_TYPE_PP(zfilename) != IS_STRING || + Z_STRLEN_PP(zfilename) == 0) { + return 0; + } + return filename_is_in_cache(Z_STRVAL_PP(zfilename), Z_STRLEN_PP(zfilename) TSRMLS_CC); +} + +static void accel_file_exists(INTERNAL_FUNCTION_PARAMETERS) +{ + if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) { + RETURN_TRUE; + } else { + orig_file_exists(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } +} + +static void accel_is_file(INTERNAL_FUNCTION_PARAMETERS) +{ + if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) { + RETURN_TRUE; + } else { + orig_is_file(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } +} + +static void accel_is_readable(INTERNAL_FUNCTION_PARAMETERS) +{ + if (accel_file_in_cache(INTERNAL_FUNCTION_PARAM_PASSTHRU)) { + RETURN_TRUE; + } else { + orig_is_readable(INTERNAL_FUNCTION_PARAM_PASSTHRU); + } +} + +static ZEND_MINIT_FUNCTION(zend_accelerator) +{ + (void)type; /* keep the compiler happy */ + + REGISTER_INI_ENTRIES(); +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zend_set_user_opcode_handler(ZEND_DECLARE_INHERITED_CLASS_DELAYED, ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER); +#endif + return SUCCESS; +} + +void zend_accel_override_file_functions(TSRMLS_D) +{ + zend_function *old_function; + if (ZCG(enabled) && accel_startup_ok && ZCG(accel_directives).file_override_enabled) { + /* override file_exists */ + if (zend_hash_find(CG(function_table), "file_exists", sizeof("file_exists"), (void **)&old_function) == SUCCESS) { + orig_file_exists = old_function->internal_function.handler; + old_function->internal_function.handler = accel_file_exists; + } + if (zend_hash_find(CG(function_table), "is_file", sizeof("is_file"), (void **)&old_function) == SUCCESS) { + orig_is_file = old_function->internal_function.handler; + old_function->internal_function.handler = accel_is_file; + } + if (zend_hash_find(CG(function_table), "is_readable", sizeof("is_readable"), (void **)&old_function) == SUCCESS) { + orig_is_readable = old_function->internal_function.handler; + old_function->internal_function.handler = accel_is_readable; + } + } +} + +static ZEND_MSHUTDOWN_FUNCTION(zend_accelerator) +{ + (void)type; /* keep the compiler happy */ + + UNREGISTER_INI_ENTRIES(); + return SUCCESS; +} + +void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS) +{ + php_info_print_table_start(); + + if (ZCG(enabled) && accel_startup_ok && (ZCG(counted) || ZCSG(accelerator_enabled))) { + php_info_print_table_row(2, "Opcode Caching", "Up and Running"); + } else { + php_info_print_table_row(2, "Opcode Caching", "Disabled"); + } + if (ZCG(enabled) && accel_startup_ok && ZCSG(accelerator_enabled) && ZCG(accel_directives).optimization_level) { + php_info_print_table_row(2, "Optimization", "Enabled"); + } else { + php_info_print_table_row(2, "Optimization", "Disabled"); + } + if (ZCG(enabled)) { + if (!accel_startup_ok || zps_api_failure_reason) { + php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason); + } else { + char buf[32]; + php_info_print_table_row(2, "Startup", "OK"); + php_info_print_table_row(2, "Shared memory model", zend_accel_get_shared_model()); + snprintf(buf, sizeof(buf), "%ld", ZCSG(hits)); + php_info_print_table_row(2, "Cache hits", buf); + snprintf(buf, sizeof(buf), "%ld", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses)); + php_info_print_table_row(2, "Cache misses", buf); + snprintf(buf, sizeof(buf), "%ld", ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory)); + php_info_print_table_row(2, "Used memory", buf); + snprintf(buf, sizeof(buf), "%ld", zend_shared_alloc_get_free_memory()); + php_info_print_table_row(2, "Free memory", buf); + snprintf(buf, sizeof(buf), "%ld", ZSMMG(wasted_shared_memory)); + php_info_print_table_row(2, "Wasted memory", buf); + snprintf(buf, sizeof(buf), "%ld", ZCSG(hash).num_direct_entries); + php_info_print_table_row(2, "Cached scripts", buf); + snprintf(buf, sizeof(buf), "%ld", ZCSG(hash).num_entries); + php_info_print_table_row(2, "Cached keys", buf); + snprintf(buf, sizeof(buf), "%ld", ZCSG(hash).max_num_entries); + php_info_print_table_row(2, "Max keys", buf); + snprintf(buf, sizeof(buf), "%ld", ZCSG(oom_restarts)); + php_info_print_table_row(2, "OOM restarts", buf); + snprintf(buf, sizeof(buf), "%ld", ZCSG(wasted_restarts)); + php_info_print_table_row(2, "Wasted memory restarts", buf); + snprintf(buf, sizeof(buf), "%ld", ZCSG(hash_restarts)); + php_info_print_table_row(2, "Hash keys restarts", buf); + snprintf(buf, sizeof(buf), "%ld", ZCSG(manual_restarts)); + php_info_print_table_row(2, "Manual restarts", buf); + } + } + + php_info_print_table_end(); + DISPLAY_INI_ENTRIES(); +} + +static zend_module_entry accel_module_entry = { + STANDARD_MODULE_HEADER, + ACCELERATOR_PRODUCT_NAME, + accel_functions, + ZEND_MINIT(zend_accelerator), + ZEND_MSHUTDOWN(zend_accelerator), + NULL, + NULL, + zend_accel_info, + ACCELERATOR_VERSION "FE", + STANDARD_MODULE_PROPERTIES +}; + +int start_accel_module(void) +{ + return zend_startup_module(&accel_module_entry); +} + +/* {{{ proto array accelerator_get_scripts() + Get the scripts which are accelerated by ZendAccelerator */ +static zval* accelerator_get_scripts(TSRMLS_D) +{ + uint i; + zval *return_value,*persistent_script_report; + zend_accel_hash_entry *cache_entry; + struct tm *ta; + struct timeval exec_time; + struct timeval fetch_time; + + if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) { + return 0; + } + + MAKE_STD_ZVAL(return_value); + array_init(return_value); + for (i = 0; i<ZCSG(hash).max_num_entries; i++) { + for (cache_entry = ZCSG(hash).hash_table[i]; cache_entry; cache_entry = cache_entry->next) { + zend_persistent_script *script; + char *str; + size_t len; + + if (cache_entry->indirect) continue; + + script = (zend_persistent_script *)cache_entry->data; + + MAKE_STD_ZVAL(persistent_script_report); + array_init(persistent_script_report); + add_assoc_stringl(persistent_script_report, "full_path", script->full_path, script->full_path_len, 1); + add_assoc_long(persistent_script_report, "hits", script->dynamic_members.hits); + add_assoc_long(persistent_script_report, "memory_consumption", script->dynamic_members.memory_consumption); + ta = localtime(&script->dynamic_members.last_used); + str = asctime(ta); + len = strlen(str); + if (len > 0 && str[len - 1] == '\n') len--; + add_assoc_stringl(persistent_script_report, "last_used", str, len, 1); + add_assoc_long(persistent_script_report, "last_used_timestamp", script->dynamic_members.last_used); + if (ZCG(accel_directives).validate_timestamps) { + add_assoc_long(persistent_script_report, "timestamp", (long)script->timestamp); + } + timerclear(&exec_time); + timerclear(&fetch_time); + + zend_hash_update(return_value->value.ht, cache_entry->key, cache_entry->key_length, &persistent_script_report, sizeof(zval *), NULL); + } + } + accelerator_shm_read_unlock(TSRMLS_C); + + return return_value; +} + +/* {{{ proto array accelerator_get_status([bool fetch_scripts]) + Obtain statistics information regarding code acceleration */ +static ZEND_FUNCTION(opcache_get_status) +{ + long reqs; + zval *memory_usage,*statistics,*scripts; + zend_bool fetch_scripts = 1; + + /* keep the compiler happy */ + (void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &fetch_scripts) == FAILURE) { + return; + } + + if (!accel_startup_ok) { + RETURN_FALSE; + } + + array_init(return_value); + + /* Trivia */ + add_assoc_bool(return_value, "opcache_enabled", ZCG(enabled) && (ZCG(counted) || ZCSG(accelerator_enabled))); + add_assoc_bool(return_value, "cache_full", ZSMMG(memory_exhausted)); + add_assoc_bool(return_value, "restart_pending", ZCSG(restart_pending)); + add_assoc_bool(return_value, "restart_in_progress", ZCSG(restart_in_progress)); + + /* Memory usage statistics */ + MAKE_STD_ZVAL(memory_usage); + array_init(memory_usage); + add_assoc_long(memory_usage, "used_memory", ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory)); + add_assoc_long(memory_usage, "free_memory", zend_shared_alloc_get_free_memory()); + add_assoc_long(memory_usage, "wasted_memory", ZSMMG(wasted_shared_memory)); + add_assoc_double(memory_usage, "current_wasted_percentage", (((double) ZSMMG(wasted_shared_memory))/ZCG(accel_directives).memory_consumption)*100.0); + add_assoc_zval(return_value, "memory_usage", memory_usage); + + /* Accelerator statistics */ + MAKE_STD_ZVAL(statistics); + array_init(statistics); + add_assoc_long(statistics, "num_cached_scripts", ZCSG(hash).num_direct_entries); + add_assoc_long(statistics, "num_cached_keys", ZCSG(hash).num_entries); + add_assoc_long(statistics, "max_cached_keys", ZCSG(hash).max_num_entries); + add_assoc_long(statistics, "hits", ZCSG(hits)); + add_assoc_long(statistics, "last_restart_time", ZCSG(last_restart_time)); + add_assoc_long(statistics, "oom_restarts", ZCSG(oom_restarts)); + add_assoc_long(statistics, "wasted_restarts", ZCSG(wasted_restarts)); + add_assoc_long(statistics, "hash_restarts", ZCSG(hash_restarts)); + add_assoc_long(statistics, "manual_restarts", ZCSG(manual_restarts)); + add_assoc_long(statistics, "misses", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses)); + add_assoc_long(statistics, "blacklist_misses", ZCSG(blacklist_misses)); + reqs = ZCSG(hits)+ZCSG(misses); + add_assoc_double(statistics, "blacklist_miss_ratio", reqs?(((double) ZCSG(blacklist_misses))/reqs)*100.0:0); + add_assoc_double(statistics, "opcache_hit_rate", reqs?(((double) ZCSG(hits))/reqs)*100.0:0); + add_assoc_zval(return_value, "opcache_statistics", statistics); + + if (fetch_scripts) { + /* accelerated scripts */ + scripts = accelerator_get_scripts(TSRMLS_C); + if (scripts) { + add_assoc_zval(return_value, "scripts", scripts); + } + } +} + +static int add_blacklist_path(zend_blacklist_entry *p, zval *return_value TSRMLS_DC) +{ + add_next_index_stringl(return_value, p->path, p->path_length, 1); + return 0; +} + +/* {{{ proto array accelerator_get_configuration() + Obtain configuration information */ +static ZEND_FUNCTION(opcache_get_configuration) +{ + zval *directives,*version,*blacklist; + + /* keep the compiler happy */ + (void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used; + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if (zend_parse_parameters_none() == FAILURE) { + RETURN_FALSE; + } +#endif + + array_init(return_value); + + /* directives */ + MAKE_STD_ZVAL(directives); + array_init(directives); + add_assoc_bool(directives, "opcache.enable", ZCG(enabled)); + add_assoc_bool(directives, "opcache.enable_cli", ZCG(accel_directives).enable_cli); + add_assoc_bool(directives, "opcache.use_cwd", ZCG(accel_directives).use_cwd); + add_assoc_bool(directives, "opcache.validate_timestamps", ZCG(accel_directives).validate_timestamps); + add_assoc_bool(directives, "opcache.inherited_hack", ZCG(accel_directives).inherited_hack); + add_assoc_bool(directives, "opcache.dups_fix", ZCG(accel_directives).ignore_dups); + add_assoc_bool(directives, "opcache.revalidate_path", ZCG(accel_directives).revalidate_path); + + add_assoc_long(directives, "opcache.log_verbosity_level", ZCG(accel_directives).log_verbosity_level); + add_assoc_long(directives, "opcache.memory_consumption", ZCG(accel_directives).memory_consumption); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + add_assoc_long(directives, "opcache.interned_strings_buffer",ZCG(accel_directives).interned_strings_buffer); +#endif + add_assoc_long(directives, "opcache.max_accelerated_files", ZCG(accel_directives).max_accelerated_files); + add_assoc_double(directives, "opcache.max_wasted_percentage", ZCG(accel_directives).max_wasted_percentage); + add_assoc_long(directives, "opcache.consistency_checks", ZCG(accel_directives).consistency_checks); + add_assoc_long(directives, "opcache.force_restart_timeout", ZCG(accel_directives).force_restart_timeout); + add_assoc_long(directives, "opcache.revalidate_freq", ZCG(accel_directives).revalidate_freq); + add_assoc_string(directives, "opcache.preferred_memory_model", STRING_NOT_NULL(ZCG(accel_directives).memory_model), 1); + add_assoc_string(directives, "opcache.blacklist_filename", STRING_NOT_NULL(ZCG(accel_directives).user_blacklist_filename), 1); + add_assoc_long(directives, "opcache.max_file_size", ZCG(accel_directives).max_file_size); + add_assoc_string(directives, "opcache.error_log", STRING_NOT_NULL(ZCG(accel_directives).error_log), 1); + + add_assoc_bool(directives, "opcache.protect_memory", ZCG(accel_directives).protect_memory); + add_assoc_bool(directives, "opcache.save_comments", ZCG(accel_directives).save_comments); + add_assoc_bool(directives, "opcache.load_comments", ZCG(accel_directives).load_comments); + add_assoc_bool(directives, "opcache.fast_shutdown", ZCG(accel_directives).fast_shutdown); + add_assoc_bool(directives, "opcache.enable_file_override", ZCG(accel_directives).file_override_enabled); + add_assoc_long(directives, "opcache.optimization_level", ZCG(accel_directives).optimization_level); + + add_assoc_zval(return_value, "directives", directives); + + /*version */ + MAKE_STD_ZVAL(version); + array_init(version); + add_assoc_string(version, "version", ACCELERATOR_VERSION, 1); + add_assoc_string(version, "opcache_product_name", ACCELERATOR_PRODUCT_NAME, 1); + add_assoc_zval(return_value, "version", version); + + /* blacklist */ + MAKE_STD_ZVAL(blacklist); + array_init(blacklist); + zend_accel_blacklist_apply(&accel_blacklist, (apply_func_arg_t) add_blacklist_path, blacklist TSRMLS_CC); + add_assoc_zval(return_value, "blacklist", blacklist); +} + +/* {{{ proto void accelerator_reset() + Request that the contents of the opcode cache to be reset */ +static ZEND_FUNCTION(opcache_reset) +{ + /* keep the compiler happy */ + (void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used; + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if (zend_parse_parameters_none() == FAILURE) { + RETURN_FALSE; + } +#endif + + if (!ZCG(enabled) || !accel_startup_ok || !ZCSG(accelerator_enabled)) { + RETURN_FALSE; + } + + zend_accel_schedule_restart(ACCEL_RESTART_USER TSRMLS_CC); + RETURN_TRUE; +} diff --git a/ext/opcache/zend_accelerator_module.h b/ext/opcache/zend_accelerator_module.h new file mode 100644 index 000000000..539b4e85a --- /dev/null +++ b/ext/opcache/zend_accelerator_module.h @@ -0,0 +1,28 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_MODULE_H +#define ZEND_ACCELERATOR_MODULE_H + +int start_accel_module(void); +void zend_accel_override_file_functions(TSRMLS_D); + +#endif /* _ZEND_ACCELERATOR_MODULE_H */ diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c new file mode 100644 index 000000000..7d5028c4c --- /dev/null +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -0,0 +1,1007 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "zend_API.h" +#include "zend_constants.h" +#include "zend_accelerator_util_funcs.h" +#include "zend_persist.h" +#include "zend_shared_alloc.h" + +#define ZEND_PROTECTED_REFCOUNT (1<<30) + +static zend_uint zend_accel_refcount = ZEND_PROTECTED_REFCOUNT; + +#if SIZEOF_SIZE_T <= SIZEOF_LONG +/* If sizeof(void*) == sizeof(ulong) we can use zend_hash index functions */ +# define accel_xlat_set(old, new) zend_hash_index_update(&ZCG(bind_hash), (ulong)(zend_uintptr_t)(old), &(new), sizeof(void*), NULL) +# define accel_xlat_get(old, new) zend_hash_index_find(&ZCG(bind_hash), (ulong)(zend_uintptr_t)(old), (void**)&(new)) +#else +# define accel_xlat_set(old, new) zend_hash_quick_add(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (ulong)(zend_uintptr_t)(old), (void**)&(new), sizeof(void*), NULL) +# define accel_xlat_get(old, new) zend_hash_quick_find(&ZCG(bind_hash), (char*)&(old), sizeof(void*), (ulong)(zend_uintptr_t)(old), (void**)&(new)) +#endif + +typedef int (*id_function_t)(void *, void *); +typedef void (*unique_copy_ctor_func_t)(void *pElement); + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +static const Bucket *uninitialized_bucket = NULL; +#endif + +static int zend_prepare_function_for_execution(zend_op_array *op_array); +static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind); + +static void zend_accel_destroy_zend_function(zend_function *function) +{ + TSRMLS_FETCH(); + + if (function->type == ZEND_USER_FUNCTION) { + if (function->op_array.static_variables) { + + efree(function->op_array.static_variables); + function->op_array.static_variables = NULL; + } + } + + destroy_zend_function(function TSRMLS_CC); +} + +static void zend_accel_destroy_zend_class(zend_class_entry **pce) +{ + zend_class_entry *ce = *pce; + + ce->function_table.pDestructor = (dtor_func_t) zend_accel_destroy_zend_function; + destroy_zend_class(pce); +} + +zend_persistent_script* create_persistent_script(void) +{ + zend_persistent_script *persistent_script = (zend_persistent_script *) emalloc(sizeof(zend_persistent_script)); + memset(persistent_script, 0, sizeof(zend_persistent_script)); + + zend_hash_init(&persistent_script->function_table, 100, NULL, (dtor_func_t) zend_accel_destroy_zend_function, 0); + /* class_table is usually destroyed by free_persistent_script() that + * overrides destructor. ZEND_CLASS_DTOR may be used by standard + * PHP compiler + */ + zend_hash_init(&persistent_script->class_table, 10, NULL, ZEND_CLASS_DTOR, 0); + + return persistent_script; +} + +void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements) +{ + if (destroy_elements) { + persistent_script->function_table.pDestructor = (dtor_func_t)zend_accel_destroy_zend_function; + persistent_script->class_table.pDestructor = (dtor_func_t)zend_accel_destroy_zend_class; + } else { + persistent_script->function_table.pDestructor = NULL; + persistent_script->class_table.pDestructor = NULL; + } + + zend_hash_destroy(&persistent_script->function_table); + zend_hash_destroy(&persistent_script->class_table); + + if (persistent_script->full_path) { + efree(persistent_script->full_path); + } + + efree(persistent_script); +} + +static int is_not_internal_function(zend_function *function) +{ + return(function->type != ZEND_INTERNAL_FUNCTION); +} + +void zend_accel_free_user_functions(HashTable *ht TSRMLS_DC) +{ + dtor_func_t orig_dtor = ht->pDestructor; + + ht->pDestructor = NULL; + zend_hash_apply(ht, (apply_func_t) is_not_internal_function TSRMLS_CC); + ht->pDestructor = orig_dtor; +} + +static int move_user_function(zend_function *function TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) +{ + HashTable *function_table = va_arg(args, HashTable *); + (void)num_args; /* keep the compiler happy */ + + if (function->type == ZEND_USER_FUNCTION) { + zend_hash_quick_update(function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, function, sizeof(zend_function), NULL); + return 1; + } else { + return 0; + } +} + +void zend_accel_move_user_functions(HashTable *src, HashTable *dst TSRMLS_DC) +{ + dtor_func_t orig_dtor = src->pDestructor; + + src->pDestructor = NULL; + zend_hash_apply_with_arguments(src TSRMLS_CC, (apply_func_args_t)move_user_function, 1, dst); + src->pDestructor = orig_dtor; +} + +static int copy_internal_function(zend_function *function, HashTable *function_table TSRMLS_DC) +{ + if (function->type == ZEND_INTERNAL_FUNCTION) { + zend_hash_update(function_table, function->common.function_name, strlen(function->common.function_name) + 1, function, sizeof(zend_function), NULL); + } + return 0; +} + +void zend_accel_copy_internal_functions(TSRMLS_D) +{ + zend_hash_apply_with_argument(CG(function_table), (apply_func_arg_t)copy_internal_function, &ZCG(function_table) TSRMLS_CC); + ZCG(internal_functions_count) = zend_hash_num_elements(&ZCG(function_table)); +} + +static void zend_destroy_property_info(zend_property_info *property_info) +{ + interned_efree((char*)property_info->name); + if (property_info->doc_comment) { + efree((char*)property_info->doc_comment); + } +} + +static inline zval* zend_clone_zval(zval *src, int bind TSRMLS_DC) +{ + zval *ret, **ret_ptr = NULL; + + if (!bind) { + ALLOC_ZVAL(ret); + *ret = *src; + INIT_PZVAL(ret); + } else if (Z_REFCOUNT_P(src) == 1) { + ALLOC_ZVAL(ret); + *ret = *src; + } else if (accel_xlat_get(src, ret_ptr) != SUCCESS) { + ALLOC_ZVAL(ret); + *ret = *src; + accel_xlat_set(src, ret); + } else { + return *ret_ptr; + } + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if ((Z_TYPE_P(ret) & IS_CONSTANT_TYPE_MASK) >= IS_ARRAY) { + switch ((Z_TYPE_P(ret) & IS_CONSTANT_TYPE_MASK)) { +#else + if ((Z_TYPE_P(ret) & ~IS_CONSTANT_INDEX) >= IS_ARRAY) { + switch ((Z_TYPE_P(ret) & ~IS_CONSTANT_INDEX)) { +#endif + case IS_STRING: + case IS_CONSTANT: + Z_STRVAL_P(ret) = (char *) interned_estrndup(Z_STRVAL_P(ret), Z_STRLEN_P(ret)); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + if (ret->value.ht && ret->value.ht != &EG(symbol_table)) { + ALLOC_HASHTABLE(ret->value.ht); + zend_hash_clone_zval(ret->value.ht, src->value.ht, 0); + } + break; + } + } + return ret; +} + +static void zend_hash_clone_zval(HashTable *ht, HashTable *source, int bind) +{ + Bucket *p, *q, **prev; + ulong nIndex; + zval *ppz; + TSRMLS_FETCH(); + + ht->nTableSize = source->nTableSize; + ht->nTableMask = source->nTableMask; + ht->nNumOfElements = source->nNumOfElements; + ht->nNextFreeElement = source->nNextFreeElement; + ht->pDestructor = ZVAL_PTR_DTOR; +#if ZEND_DEBUG + ht->inconsistent = 0; +#endif + ht->persistent = 0; + ht->arBuckets = NULL; + ht->pListHead = NULL; + ht->pListTail = NULL; + ht->pInternalPointer = NULL; + ht->nApplyCount = 0; + ht->bApplyProtection = 1; + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!ht->nTableMask) { + ht->arBuckets = (Bucket**)&uninitialized_bucket; + return; + } +#endif + + ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *)); + + prev = &ht->pListHead; + p = source->pListHead; + while (p) { + nIndex = p->h & ht->nTableMask; + + /* Create bucket and initialize key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!p->nKeyLength) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = NULL; + } else if (IS_INTERNED(p->arKey)) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = p->arKey; + } else { + q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength); + q->arKey = ((char*)q) + sizeof(Bucket); + memcpy((char*)q->arKey, p->arKey, p->nKeyLength); + } +#else + q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength); + if (p->nKeyLength) { + memcpy(q->arKey, p->arKey, p->nKeyLength); + } +#endif + q->h = p->h; + q->nKeyLength = p->nKeyLength; + + /* Insert into hash collision list */ + q->pNext = ht->arBuckets[nIndex]; + q->pLast = NULL; + if (q->pNext) { + q->pNext->pLast = q; + } + ht->arBuckets[nIndex] = q; + + /* Insert into global list */ + q->pListLast = ht->pListTail; + ht->pListTail = q; + q->pListNext = NULL; + *prev = q; + prev = &q->pListNext; + + /* Copy data */ + q->pData = &q->pDataPtr; + if (!bind) { + ALLOC_ZVAL(ppz); + *ppz = *((zval*)p->pDataPtr); + INIT_PZVAL(ppz); + } else if (Z_REFCOUNT_P((zval*)p->pDataPtr) == 1) { + ALLOC_ZVAL(ppz); + *ppz = *((zval*)p->pDataPtr); + } else if (accel_xlat_get(p->pDataPtr, ppz) != SUCCESS) { + ALLOC_ZVAL(ppz); + *ppz = *((zval*)p->pDataPtr); + accel_xlat_set(p->pDataPtr, ppz); + } else { + q->pDataPtr = *(void**)ppz; + p = p->pListNext; + continue; + } + q->pDataPtr = (void*)ppz; + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if ((Z_TYPE_P((zval*)p->pDataPtr) & IS_CONSTANT_TYPE_MASK) >= IS_ARRAY) { + switch ((Z_TYPE_P((zval*)p->pDataPtr) & IS_CONSTANT_TYPE_MASK)) { +#else + if ((Z_TYPE_P((zval*)p->pDataPtr) & ~IS_CONSTANT_INDEX) >= IS_ARRAY) { + switch ((Z_TYPE_P((zval*)p->pDataPtr) & ~IS_CONSTANT_INDEX)) { +#endif + case IS_STRING: + case IS_CONSTANT: + Z_STRVAL_P(ppz) = (char *) interned_estrndup(Z_STRVAL_P((zval*)p->pDataPtr), Z_STRLEN_P((zval*)p->pDataPtr)); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + if (((zval*)p->pDataPtr)->value.ht && ((zval*)p->pDataPtr)->value.ht != &EG(symbol_table)) { + ALLOC_HASHTABLE(ppz->value.ht); + zend_hash_clone_zval(ppz->value.ht, ((zval*)p->pDataPtr)->value.ht, 0); + } + break; + } + } + + p = p->pListNext; + } + ht->pInternalPointer = ht->pListHead; +} + +static void zend_hash_clone_methods(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce TSRMLS_DC) +{ + Bucket *p, *q, **prev; + ulong nIndex; + zend_class_entry **new_ce; + zend_function** new_prototype; + zend_op_array *new_entry; + + ht->nTableSize = source->nTableSize; + ht->nTableMask = source->nTableMask; + ht->nNumOfElements = source->nNumOfElements; + ht->nNextFreeElement = source->nNextFreeElement; + ht->pDestructor = ZEND_FUNCTION_DTOR; +#if ZEND_DEBUG + ht->inconsistent = 0; +#endif + ht->persistent = 0; + ht->pListHead = NULL; + ht->pListTail = NULL; + ht->pInternalPointer = NULL; + ht->nApplyCount = 0; + ht->bApplyProtection = 1; + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!ht->nTableMask) { + ht->arBuckets = (Bucket**)&uninitialized_bucket; + return; + } +#endif + + ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *)); + + prev = &ht->pListHead; + p = source->pListHead; + while (p) { + nIndex = p->h & ht->nTableMask; + + /* Create bucket and initialize key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!p->nKeyLength) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = NULL; + } else if (IS_INTERNED(p->arKey)) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = p->arKey; + } else { + q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength); + q->arKey = ((char*)q) + sizeof(Bucket); + memcpy((char*)q->arKey, p->arKey, p->nKeyLength); + } +#else + q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength); + if (p->nKeyLength) { + memcpy(q->arKey, p->arKey, p->nKeyLength); + } +#endif + q->h = p->h; + q->nKeyLength = p->nKeyLength; + + /* Insert into hash collision list */ + q->pNext = ht->arBuckets[nIndex]; + q->pLast = NULL; + if (q->pNext) { + q->pNext->pLast = q; + } + ht->arBuckets[nIndex] = q; + + /* Insert into global list */ + q->pListLast = ht->pListTail; + ht->pListTail = q; + q->pListNext = NULL; + *prev = q; + prev = &q->pListNext; + + /* Copy data */ + q->pData = (void *) emalloc(sizeof(zend_function)); + new_entry = (zend_op_array*)q->pData; + *new_entry = *(zend_op_array*)p->pData; + q->pDataPtr = NULL; + + /* Copy constructor */ + /* we use refcount to show that op_array is referenced from several places */ + if (new_entry->refcount != NULL) { + accel_xlat_set(p->pData, new_entry); + } + + zend_prepare_function_for_execution(new_entry); + + if (old_ce == new_entry->scope) { + new_entry->scope = ce; + } else { + if (accel_xlat_get(new_entry->scope, new_ce) == SUCCESS) { + new_entry->scope = *new_ce; + } else { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s, function %s. Please call Zend Support", ce->name, new_entry->function_name); + } + } + + /* update prototype */ + if (new_entry->prototype) { + if (accel_xlat_get(new_entry->prototype, new_prototype) == SUCCESS) { + new_entry->prototype = *new_prototype; + } else { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s, function %s. Please call Zend Support", ce->name, new_entry->function_name); + } + } + + p = p->pListNext; + } + ht->pInternalPointer = ht->pListHead; +} + +static void zend_hash_clone_prop_info(HashTable *ht, HashTable *source, zend_class_entry *old_ce, zend_class_entry *ce TSRMLS_DC) +{ + Bucket *p, *q, **prev; + ulong nIndex; + zend_class_entry **new_ce; + zend_property_info *prop_info; + + ht->nTableSize = source->nTableSize; + ht->nTableMask = source->nTableMask; + ht->nNumOfElements = source->nNumOfElements; + ht->nNextFreeElement = source->nNextFreeElement; + ht->pDestructor = (dtor_func_t) zend_destroy_property_info; +#if ZEND_DEBUG + ht->inconsistent = 0; +#endif + ht->persistent = 0; + ht->pListHead = NULL; + ht->pListTail = NULL; + ht->pInternalPointer = NULL; + ht->nApplyCount = 0; + ht->bApplyProtection = 1; + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!ht->nTableMask) { + ht->arBuckets = (Bucket**)&uninitialized_bucket; + return; + } +#endif + + ht->arBuckets = (Bucket **) ecalloc(ht->nTableSize, sizeof(Bucket *)); + + prev = &ht->pListHead; + p = source->pListHead; + while (p) { + nIndex = p->h & ht->nTableMask; + + /* Create bucket and initialize key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (!p->nKeyLength) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = NULL; + } else if (IS_INTERNED(p->arKey)) { + q = (Bucket *) emalloc(sizeof(Bucket)); + q->arKey = p->arKey; + } else { + q = (Bucket *) emalloc(sizeof(Bucket) + p->nKeyLength); + q->arKey = ((char*)q) + sizeof(Bucket); + memcpy((char*)q->arKey, p->arKey, p->nKeyLength); + } +#else + q = (Bucket *) emalloc(sizeof(Bucket) - 1 + p->nKeyLength); + if (p->nKeyLength) { + memcpy(q->arKey, p->arKey, p->nKeyLength); + } +#endif + q->h = p->h; + q->nKeyLength = p->nKeyLength; + + /* Insert into hash collision list */ + q->pNext = ht->arBuckets[nIndex]; + q->pLast = NULL; + if (q->pNext) { + q->pNext->pLast = q; + } + ht->arBuckets[nIndex] = q; + + /* Insert into global list */ + q->pListLast = ht->pListTail; + ht->pListTail = q; + q->pListNext = NULL; + *prev = q; + prev = &q->pListNext; + + /* Copy data */ + q->pData = (void *) emalloc(sizeof(zend_property_info)); + prop_info = q->pData; + *prop_info = *(zend_property_info*)p->pData; + q->pDataPtr = NULL; + + /* Copy constructor */ + prop_info->name = interned_estrndup(prop_info->name, prop_info->name_length); + if (prop_info->doc_comment) { + if (ZCG(accel_directives).load_comments) { + prop_info->doc_comment = estrndup(prop_info->doc_comment, prop_info->doc_comment_len); + } else { + prop_info->doc_comment = NULL; + } + } + if (prop_info->ce == old_ce) { + prop_info->ce = ce; + } else if (accel_xlat_get(prop_info->ce, new_ce) == SUCCESS) { + prop_info->ce = *new_ce; + } else { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME" class loading error, class %s, property %s. Please call Zend Support", ce->name, prop_info->name); + } + + p = p->pListNext; + } + ht->pInternalPointer = ht->pListHead; +} + +/* protects reference count, creates copy of statics */ +static int zend_prepare_function_for_execution(zend_op_array *op_array) +{ + HashTable *shared_statics = op_array->static_variables; + + /* protect reference count */ + op_array->refcount = &zend_accel_refcount; + (*op_array->refcount) = ZEND_PROTECTED_REFCOUNT; + + /* copy statics */ + if (shared_statics) { + ALLOC_HASHTABLE(op_array->static_variables); + zend_hash_clone_zval(op_array->static_variables, shared_statics, 0); + } + + return 0; +} + +#define zend_update_inherited_handler(handler) \ +{ \ + if (ce->handler != NULL) { \ + if (accel_xlat_get(ce->handler, new_func) == SUCCESS) { \ + ce->handler = *new_func; \ + } else { \ + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME " class loading error, class %s. Please call Zend Support", ce->name); \ + } \ + } \ +} + +/* Protects class' refcount, copies default properties, functions and class name */ +static void zend_class_copy_ctor(zend_class_entry **pce) +{ + zend_class_entry *ce = *pce; + zend_class_entry *old_ce = ce; + zend_class_entry **new_ce; + zend_function **new_func; + TSRMLS_FETCH(); + + *pce = ce = emalloc(sizeof(zend_class_entry)); + *ce = *old_ce; + ce->refcount = 1; + + if (old_ce->refcount != 1) { + /* this class is not used as a parent for any other classes */ + accel_xlat_set(old_ce, ce); + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (old_ce->default_properties_table) { + int i; + + ce->default_properties_table = emalloc(sizeof(zval*) * old_ce->default_properties_count); + for (i = 0; i < old_ce->default_properties_count; i++) { + if (old_ce->default_properties_table[i]) { + ce->default_properties_table[i] = zend_clone_zval(old_ce->default_properties_table[i], 0 TSRMLS_CC); + } else { + ce->default_properties_table[i] = NULL; + } + } + } +#else + zend_hash_clone_zval(&ce->default_properties, &old_ce->default_properties, 0); +#endif + + zend_hash_clone_methods(&ce->function_table, &old_ce->function_table, old_ce, ce TSRMLS_CC); + + /* static members */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (old_ce->default_static_members_table) { + int i; + + ce->default_static_members_table = emalloc(sizeof(zval*) * old_ce->default_static_members_count); + for (i = 0; i < old_ce->default_static_members_count; i++) { + if (old_ce->default_static_members_table[i]) { + ce->default_static_members_table[i] = zend_clone_zval(old_ce->default_static_members_table[i], 1 TSRMLS_CC); + } else { + ce->default_static_members_table[i] = NULL; + } + } + } + ce->static_members_table = ce->default_static_members_table; +#else + zend_hash_clone_zval(&ce->default_static_members, &old_ce->default_static_members, 1); + ce->static_members = &ce->default_static_members; +#endif + + /* properties_info */ + zend_hash_clone_prop_info(&ce->properties_info, &old_ce->properties_info, old_ce, ce TSRMLS_CC); + + /* constants table */ + zend_hash_clone_zval(&ce->constants_table, &old_ce->constants_table, 0); + + ce->name = interned_estrndup(ce->name, ce->name_length); + + /* interfaces aren't really implemented, so we create a new table */ + if (ce->num_interfaces) { + ce->interfaces = emalloc(sizeof(zend_class_entry *) * ce->num_interfaces); + memset(ce->interfaces, 0, sizeof(zend_class_entry *) * ce->num_interfaces); + } else { + ce->interfaces = NULL; + } + if (ZEND_CE_DOC_COMMENT(ce)) { + if (ZCG(accel_directives).load_comments) { + ZEND_CE_DOC_COMMENT(ce) = estrndup(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce)); + } else { + ZEND_CE_DOC_COMMENT(ce) = NULL; + } + } + + if (ce->parent) { + if (accel_xlat_get(ce->parent, new_ce) == SUCCESS) { + ce->parent = *new_ce; + } else { + zend_error(E_ERROR, ACCELERATOR_PRODUCT_NAME" class loading error, class %s. Please call Zend Support", ce->name); + } + } + + zend_update_inherited_handler(constructor); + zend_update_inherited_handler(destructor); + zend_update_inherited_handler(clone); + zend_update_inherited_handler(__get); + zend_update_inherited_handler(__set); + zend_update_inherited_handler(__call); +/* 5.1 stuff */ + zend_update_inherited_handler(serialize_func); + zend_update_inherited_handler(unserialize_func); + zend_update_inherited_handler(__isset); + zend_update_inherited_handler(__unset); +/* 5.2 stuff */ + zend_update_inherited_handler(__tostring); + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO +/* 5.3 stuff */ + zend_update_inherited_handler(__callstatic); +#endif + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +/* 5.4 traits */ + if (ce->trait_aliases) { + zend_trait_alias **trait_aliases; + int i = 0; + + while (ce->trait_aliases[i]) { + i++; + } + trait_aliases = emalloc(sizeof(zend_trait_alias*) * (i + 1)); + i = 0; + while (ce->trait_aliases[i]) { + trait_aliases[i] = emalloc(sizeof(zend_trait_alias)); + memcpy(trait_aliases[i], ce->trait_aliases[i], sizeof(zend_trait_alias)); + trait_aliases[i]->trait_method = emalloc(sizeof(zend_trait_method_reference)); + memcpy(trait_aliases[i]->trait_method, ce->trait_aliases[i]->trait_method, sizeof(zend_trait_method_reference)); + if (trait_aliases[i]->trait_method) { + if (trait_aliases[i]->trait_method->method_name) { + trait_aliases[i]->trait_method->method_name = + estrndup(trait_aliases[i]->trait_method->method_name, + trait_aliases[i]->trait_method->mname_len); + } + if (trait_aliases[i]->trait_method->class_name) { + trait_aliases[i]->trait_method->class_name = + estrndup(trait_aliases[i]->trait_method->class_name, + trait_aliases[i]->trait_method->cname_len); + } + } + + if (trait_aliases[i]->alias) { + trait_aliases[i]->alias = + estrndup(trait_aliases[i]->alias, + trait_aliases[i]->alias_len); + } + i++; + } + trait_aliases[i] = NULL; + ce->trait_aliases = trait_aliases; + } + + if (ce->trait_precedences) { + zend_trait_precedence **trait_precedences; + int i = 0; + + while (ce->trait_precedences[i]) { + i++; + } + trait_precedences = emalloc(sizeof(zend_trait_precedence*) * (i + 1)); + i = 0; + while (ce->trait_precedences[i]) { + trait_precedences[i] = emalloc(sizeof(zend_trait_precedence)); + memcpy(trait_precedences[i], ce->trait_precedences[i], sizeof(zend_trait_precedence)); + trait_precedences[i]->trait_method = emalloc(sizeof(zend_trait_method_reference)); + memcpy(trait_precedences[i]->trait_method, ce->trait_precedences[i]->trait_method, sizeof(zend_trait_method_reference)); + + trait_precedences[i]->trait_method->method_name = + estrndup(trait_precedences[i]->trait_method->method_name, + trait_precedences[i]->trait_method->mname_len); + trait_precedences[i]->trait_method->class_name = + estrndup(trait_precedences[i]->trait_method->class_name, + trait_precedences[i]->trait_method->cname_len); + + if (trait_precedences[i]->exclude_from_classes) { + zend_class_entry **exclude_from_classes; + int j = 0; + + while (trait_precedences[i]->exclude_from_classes[j]) { + j++; + } + exclude_from_classes = emalloc(sizeof(zend_class_entry*) * (j + 1)); + j = 0; + while (trait_precedences[i]->exclude_from_classes[j]) { + exclude_from_classes[j] = (zend_class_entry*)estrndup( + (char*)trait_precedences[i]->exclude_from_classes[j], + strlen((char*)trait_precedences[i]->exclude_from_classes[j])); + j++; + } + exclude_from_classes[j] = NULL; + trait_precedences[i]->exclude_from_classes = exclude_from_classes; + } + i++; + } + trait_precedences[i] = NULL; + ce->trait_precedences = trait_precedences; + } +#endif +} + +static int zend_hash_unique_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor, uint size, int ignore_dups, void **fail_data, void **conflict_data) +{ + Bucket *p; + void *t; + + p = source->pListHead; + while (p) { + if (p->nKeyLength > 0) { + if (zend_hash_quick_add(target, p->arKey, p->nKeyLength, p->h, p->pData, size, &t) == SUCCESS) { + if (pCopyConstructor) { + pCopyConstructor(t); + } + } else { + if (p->nKeyLength > 0 && p->arKey[0] == 0) { + /* Mangled key, ignore and wait for runtime */ + } else if (!ignore_dups && zend_hash_quick_find(target, p->arKey, p->nKeyLength, p->h, &t) == SUCCESS) { + *fail_data = p->pData; + *conflict_data = t; + return FAILURE; + } + } + } else { + if (!zend_hash_index_exists(target, p->h) && zend_hash_index_update(target, p->h, p->pData, size, &t) == SUCCESS) { + if (pCopyConstructor) { + pCopyConstructor(t); + } + } else if (!ignore_dups && zend_hash_index_find(target,p->h, &t) == SUCCESS) { + *fail_data = p->pData; + *conflict_data = t; + return FAILURE; + } + } + p = p->pListNext; + } + target->pInternalPointer = target->pListHead; + + return SUCCESS; +} + +static void zend_accel_function_hash_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor) +{ + zend_function *function1, *function2; + TSRMLS_FETCH(); + + if (zend_hash_unique_copy(target, source, pCopyConstructor, sizeof(zend_function), 0, (void**)&function1, (void**)&function2) != SUCCESS) { + CG(in_compilation) = 1; + zend_set_compiled_filename(function1->op_array.filename TSRMLS_CC); + CG(zend_lineno) = function1->op_array.opcodes[0].lineno; + if (function2->type == ZEND_USER_FUNCTION + && function2->op_array.last > 0) { + zend_error(E_ERROR, "Cannot redeclare %s() (previously declared in %s:%d)", + function1->common.function_name, + function2->op_array.filename, + (int)function2->op_array.opcodes[0].lineno); + } else { + zend_error(E_ERROR, "Cannot redeclare %s()", function1->common.function_name); + } + } +} + +static void zend_accel_class_hash_copy(HashTable *target, HashTable *source, unique_copy_ctor_func_t pCopyConstructor TSRMLS_DC) +{ + zend_class_entry **pce1, **pce2; + + if (zend_hash_unique_copy(target, source, pCopyConstructor, sizeof(zend_class_entry*), ZCG(accel_directives).ignore_dups, (void**)&pce1, (void**)&pce2) != SUCCESS) { + CG(in_compilation) = 1; +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + zend_set_compiled_filename((*pce1)->info.user.filename TSRMLS_CC); + CG(zend_lineno) = (*pce1)->info.user.line_start; +#else + zend_set_compiled_filename((*pce1)->filename TSRMLS_CC); + CG(zend_lineno) = (*pce1)->line_start; +#endif + zend_error(E_ERROR, "Cannot redeclare class %s", (*pce1)->name); + } +} + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO +static void zend_do_delayed_early_binding(zend_op_array *op_array, zend_uint early_binding TSRMLS_DC) +{ + zend_uint opline_num = early_binding; + + if ((int)opline_num != -1) { + zend_bool orig_in_compilation = CG(in_compilation); + char *orig_compiled_filename = zend_set_compiled_filename(op_array->filename TSRMLS_CC); + zend_class_entry **pce; + + CG(in_compilation) = 1; + while ((int)opline_num != -1) { + if (zend_lookup_class(Z_STRVAL(op_array->opcodes[opline_num - 1].op2.u.constant), Z_STRLEN(op_array->opcodes[opline_num - 1].op2.u.constant), &pce TSRMLS_CC) == SUCCESS) { + do_bind_inherited_class(&op_array->opcodes[opline_num], EG(class_table), *pce, 1 TSRMLS_CC); + } + opline_num = op_array->opcodes[opline_num].result.u.opline_num; + } + zend_restore_compiled_filename(orig_compiled_filename); + CG(in_compilation) = orig_in_compilation; + } +} +#endif + +zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory TSRMLS_DC) +{ + zend_op_array *op_array; + + op_array = (zend_op_array *) emalloc(sizeof(zend_op_array)); + *op_array = persistent_script->main_op_array; + + if (from_shared_memory) { + /* Copy all the necessary stuff from shared memory to regular memory, and protect the shared script */ + if (zend_hash_num_elements(&persistent_script->class_table) > 0) { + zend_hash_init(&ZCG(bind_hash), 10, NULL, NULL, 0); + zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, (unique_copy_ctor_func_t) zend_class_copy_ctor TSRMLS_CC); + zend_hash_destroy(&ZCG(bind_hash)); + } + /* we must first to copy all classes and then prepare functions, since functions may try to bind + classes - which depend on pre-bind class entries existant in the class table */ + if (zend_hash_num_elements(&persistent_script->function_table) > 0) { + zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table, (unique_copy_ctor_func_t)zend_prepare_function_for_execution); + } + + zend_prepare_function_for_execution(op_array); + + /* Register __COMPILER_HALT_OFFSET__ constant */ + if (persistent_script->compiler_halt_offset != 0 && + persistent_script->full_path) { + char *name, *cfilename; + char haltoff[] = "__COMPILER_HALT_OFFSET__"; + int len, clen; + + cfilename = persistent_script->full_path; + clen = strlen(cfilename); + zend_mangle_property_name(&name, &len, haltoff, sizeof(haltoff) - 1, cfilename, clen, 0); + if (!zend_hash_exists(EG(zend_constants), name, len + 1)) { + zend_register_long_constant(name, len + 1, persistent_script->compiler_halt_offset, CONST_CS, 0 TSRMLS_CC); + } + efree(name); + } + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if ((int)persistent_script->early_binding != -1) { + zend_do_delayed_early_binding(op_array, persistent_script->early_binding TSRMLS_CC); + } +#endif + + } else /* if (!from_shared_memory) */ { + if (zend_hash_num_elements(&persistent_script->function_table) > 0) { + zend_accel_function_hash_copy(CG(function_table), &persistent_script->function_table, NULL); + } + if (zend_hash_num_elements(&persistent_script->class_table) > 0) { + zend_accel_class_hash_copy(CG(class_table), &persistent_script->class_table, NULL TSRMLS_CC); + } + free_persistent_script(persistent_script, 0); /* free only hashes */ + } + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if (op_array->early_binding != (zend_uint)-1) { + char *orig_compiled_filename = CG(compiled_filename); + CG(compiled_filename) = persistent_script->full_path; + zend_do_delayed_early_binding(op_array TSRMLS_CC); + CG(compiled_filename) = orig_compiled_filename; + } +#endif + + return op_array; +} + +/* + * zend_adler32() is based on zlib implementation + * Computes the Adler-32 checksum of a data stream + * + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + */ + +#define ADLER32_BASE 65521 /* largest prime smaller than 65536 */ +#define ADLER32_NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define ADLER32_DO1(buf) {s1 += *(buf); s2 += s1;} +#define ADLER32_DO2(buf, i) ADLER32_DO1(buf + i); ADLER32_DO1(buf + i + 1); +#define ADLER32_DO4(buf, i) ADLER32_DO2(buf, i); ADLER32_DO2(buf, i + 2); +#define ADLER32_DO8(buf, i) ADLER32_DO4(buf, i); ADLER32_DO4(buf, i + 4); +#define ADLER32_DO16(buf) ADLER32_DO8(buf, 0); ADLER32_DO8(buf, 8); + +unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len) +{ + unsigned int s1 = checksum & 0xffff; + unsigned int s2 = (checksum >> 16) & 0xffff; + signed char *end; + + while (len >= ADLER32_NMAX) { + len -= ADLER32_NMAX; + end = buf + ADLER32_NMAX; + do { + ADLER32_DO16(buf); + buf += 16; + } while (buf != end); + s1 %= ADLER32_BASE; + s2 %= ADLER32_BASE; + } + + if (len) { + if (len >= 16) { + end = buf + (len & 0xfff0); + len &= 0xf; + do { + ADLER32_DO16(buf); + buf += 16; + } while (buf != end); + } + if (len) { + end = buf + len; + do { + ADLER32_DO1(buf); + buf++; + } while (buf != end); + } + s1 %= ADLER32_BASE; + s2 %= ADLER32_BASE; + } + + return (s2 << 16) | s1; +} diff --git a/ext/opcache/zend_accelerator_util_funcs.h b/ext/opcache/zend_accelerator_util_funcs.h new file mode 100644 index 000000000..a926145c9 --- /dev/null +++ b/ext/opcache/zend_accelerator_util_funcs.h @@ -0,0 +1,49 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_UTIL_FUNCS_H +#define ZEND_ACCELERATOR_UTIL_FUNCS_H + +#include "zend.h" +#include "ZendAccelerator.h" + +void zend_accel_copy_internal_functions(TSRMLS_D); + +zend_persistent_script* create_persistent_script(void); +void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements); + +void zend_accel_free_user_functions(HashTable *ht TSRMLS_DC); +void zend_accel_move_user_functions(HashTable *str, HashTable *dst TSRMLS_DC); + +zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory TSRMLS_DC); + +#define ADLER32_INIT 1 /* initial Adler-32 value */ + +unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len); + +#endif /* ZEND_ACCELERATOR_UTIL_FUNCS_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c new file mode 100644 index 000000000..0bffae4d0 --- /dev/null +++ b/ext/opcache/zend_persist.c @@ -0,0 +1,680 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "zend.h" +#include "ZendAccelerator.h" +#include "zend_persist.h" +#include "zend_extensions.h" +#include "zend_shared_alloc.h" +#include "zend_vm.h" +#include "zend_constants.h" +#include "zend_operators.h" + +#define zend_accel_store(p, size) \ + (p = _zend_shared_memdup((void*)p, size, 1 TSRMLS_CC)) +#define zend_accel_memdup(p, size) \ + _zend_shared_memdup((void*)p, size, 0 TSRMLS_CC) + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +# define zend_accel_memdup_interned_string(str, len) \ + IS_INTERNED(str) ? str : zend_accel_memdup(str, len) + +# define zend_accel_store_interned_string(str, len) do { \ + if (!IS_INTERNED(str)) { zend_accel_store(str, len); } \ + } while (0) +#else +# define zend_accel_memdup_interned_string(str, len) \ + zend_accel_memdup(str, len) + +# define zend_accel_store_interned_string(str, len) \ + zend_accel_store(str, len) +#endif + +typedef void (*zend_persist_func_t)(void * TSRMLS_DC); + +static void zend_persist_zval_ptr(zval **zp TSRMLS_DC); + +static void zend_hash_persist(HashTable *ht, void (*pPersistElement)(void *pElement TSRMLS_DC), size_t el_size TSRMLS_DC) +{ + Bucket *p = ht->pListHead; + uint i; + + while (p) { + Bucket *q = p; + + /* persist bucket and key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + p = zend_accel_memdup(p, sizeof(Bucket)); + if (p->nKeyLength) { + p->arKey = zend_accel_memdup_interned_string(p->arKey, p->nKeyLength); + } +#else + p = zend_accel_memdup(p, sizeof(Bucket) - 1 + p->nKeyLength); +#endif + + /* persist data pointer in bucket */ + if (!p->pDataPtr) { + zend_accel_store(p->pData, el_size); + } else { + /* Update p->pData to point to the new p->pDataPtr address, after the bucket relocation */ + p->pData = &p->pDataPtr; + } + + /* persist the data itself */ + if (pPersistElement) { + pPersistElement(p->pData TSRMLS_CC); + } + + /* update linked lists */ + if (p->pLast) { + p->pLast->pNext = p; + } + if (p->pNext) { + p->pNext->pLast = p; + } + if (p->pListLast) { + p->pListLast->pListNext = p; + } + if (p->pListNext) { + p->pListNext->pListLast = p; + } + + p = p->pListNext; + + /* delete the old non-persistent bucket */ + efree(q); + } + + /* update linked lists */ + if (ht->pListHead) { + ht->pListHead = zend_shared_alloc_get_xlat_entry(ht->pListHead); + } + if (ht->pListTail) { + ht->pListTail = zend_shared_alloc_get_xlat_entry(ht->pListTail); + } + if (ht->pInternalPointer) { + ht->pInternalPointer = zend_shared_alloc_get_xlat_entry(ht->pInternalPointer); + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + /* Check if HastTable is initialized */ + if (ht->nTableMask) { +#endif + if (ht->nNumOfElements) { + /* update hash table */ + for (i = 0; i < ht->nTableSize; i++) { + if (ht->arBuckets[i]) { + ht->arBuckets[i] = zend_shared_alloc_get_xlat_entry(ht->arBuckets[i]); + } + } + } + zend_accel_store(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + } else { + ht->arBuckets = NULL; + } +#endif +} + +static void zend_persist_zval(zval *z TSRMLS_DC) +{ +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + switch (z->type & IS_CONSTANT_TYPE_MASK) { +#else + switch (z->type & ~IS_CONSTANT_INDEX) { +#endif + case IS_STRING: + case IS_CONSTANT: + zend_accel_store_interned_string(z->value.str.val, z->value.str.len + 1); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + zend_accel_store(z->value.ht, sizeof(HashTable)); + zend_hash_persist(z->value.ht, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + break; + } +} + +static void zend_persist_zval_ptr(zval **zp TSRMLS_DC) +{ + zval *new_ptr = zend_shared_alloc_get_xlat_entry(*zp); + + if (new_ptr) { + *zp = new_ptr; + } else { + /* Attempt to store only if we didn't store this zval_ptr yet */ + zend_accel_store(*zp, sizeof(zval)); + zend_persist_zval(*zp TSRMLS_CC); + } +} + +static void zend_protect_zval(zval *z TSRMLS_DC) +{ + PZ_SET_ISREF_P(z); + PZ_SET_REFCOUNT_P(z, 2); +} + +static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script TSRMLS_DC) +{ + zend_op *persist_ptr; +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + int has_jmp = 0; +#endif +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + zend_literal *orig_literals = NULL; +#endif + + if (op_array->type != ZEND_USER_FUNCTION) { + return; + } + +#if ZEND_EXTENSION_API_NO <= PHP_5_3_X_API_NO + op_array->size = op_array->last; +#endif + + if (--(*op_array->refcount) == 0) { + efree(op_array->refcount); + } + op_array->refcount = NULL; + + if (op_array->filename) { + /* do not free! PHP has centralized filename storage, compiler will free it */ + op_array->filename = zend_accel_memdup(op_array->filename, strlen(op_array->filename) + 1); + } + + if (main_persistent_script) { + zend_bool orig_in_execution = EG(in_execution); + zend_op_array *orig_op_array = EG(active_op_array); + zval offset; + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + main_persistent_script->early_binding = -1; +#endif + EG(in_execution) = 1; + EG(active_op_array) = op_array; + if (zend_get_constant("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1, &offset TSRMLS_CC)) { + main_persistent_script->compiler_halt_offset = Z_LVAL(offset); + } + EG(active_op_array) = orig_op_array; + EG(in_execution) = orig_in_execution; + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (op_array->literals) { + orig_literals = zend_shared_alloc_get_xlat_entry(op_array->literals); + if (orig_literals) { + op_array->literals = orig_literals; + } else { + zend_literal *p = zend_accel_memdup(op_array->literals, sizeof(zend_literal) * op_array->last_literal); + zend_literal *end = p + op_array->last_literal; + orig_literals = op_array->literals; + op_array->literals = p; + while (p < end) { + zend_persist_zval(&p->constant TSRMLS_CC); + zend_protect_zval(&p->constant TSRMLS_CC); + p++; + } + efree(orig_literals); + } + } +#endif + + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->opcodes))) { + op_array->opcodes = persist_ptr; + } else { + zend_op *new_opcodes = zend_accel_memdup(op_array->opcodes, sizeof(zend_op) * op_array->last); + zend_op *opline = new_opcodes; + zend_op *end = new_opcodes + op_array->last; + int offset = 0; + + for (; opline < end ; opline++, offset++) { + if (ZEND_OP1_TYPE(opline) == IS_CONST) { +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals)); +#else + zend_persist_zval(&opline->op1.u.constant TSRMLS_CC); + zend_protect_zval(&opline->op1.u.constant TSRMLS_CC); +#endif + } + if (ZEND_OP2_TYPE(opline) == IS_CONST) { +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)orig_literals)); +#else + zend_persist_zval(&opline->op2.u.constant TSRMLS_CC); + zend_protect_zval(&opline->op2.u.constant TSRMLS_CC); +#endif + } + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + switch (opline->opcode) { + case ZEND_JMP: + has_jmp = 1; + if (ZEND_DONE_PASS_TWO(op_array)) { + ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes]; + } + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + has_jmp = 1; + if (ZEND_DONE_PASS_TWO(op_array)) { + ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes]; + } + break; + case ZEND_JMPZNZ: + case ZEND_BRK: + case ZEND_CONT: + has_jmp = 1; + break; + case ZEND_DECLARE_INHERITED_CLASS: + if (main_persistent_script && ZCG(accel_directives).inherited_hack) { + if (!has_jmp && + ((opline + 2) >= end || + (opline + 1)->opcode != ZEND_FETCH_CLASS || + (opline + 2)->opcode != ZEND_ADD_INTERFACE)) { + + zend_uint *opline_num = &main_persistent_script->early_binding; + + while ((int)*opline_num != -1) { + opline_num = &new_opcodes[*opline_num].result.u.opline_num; + } + *opline_num = opline - new_opcodes; + opline->result.op_type = IS_UNUSED; + opline->result.u.opline_num = -1; + opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED; + ZEND_VM_SET_OPCODE_HANDLER(opline); + } + break; + } + } + +#else /* if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO */ + + if (ZEND_DONE_PASS_TWO(op_array)) { + /* fix jumps to point to new array */ + switch (opline->opcode) { + case ZEND_JMP: + case ZEND_GOTO: +#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO + case ZEND_FAST_CALL: +#endif + ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes]; + break; + case ZEND_JMPZ: + case ZEND_JMPNZ: + case ZEND_JMPZ_EX: + case ZEND_JMPNZ_EX: + case ZEND_JMP_SET: +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + case ZEND_JMP_SET_VAR: +#endif + ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes]; + break; + } + } +#endif /* if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO */ + } + + efree(op_array->opcodes); + op_array->opcodes = new_opcodes; + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (op_array->run_time_cache) { + efree(op_array->run_time_cache); + op_array->run_time_cache = NULL; + } +#endif + } + + if (op_array->function_name) { + char *new_name; + if ((new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name))) { + op_array->function_name = new_name; + } else { + zend_accel_store(op_array->function_name, strlen(op_array->function_name) + 1); + } + } + + if (op_array->arg_info) { + zend_arg_info *new_ptr; + if ((new_ptr = zend_shared_alloc_get_xlat_entry(op_array->arg_info))) { + op_array->arg_info = new_ptr; + } else { + zend_uint i; + + zend_accel_store(op_array->arg_info, sizeof(zend_arg_info) * op_array->num_args); + for (i = 0; i < op_array->num_args; i++) { + if (op_array->arg_info[i].name) { + zend_accel_store_interned_string(op_array->arg_info[i].name, op_array->arg_info[i].name_len + 1); + } + if (op_array->arg_info[i].class_name) { + zend_accel_store_interned_string(op_array->arg_info[i].class_name, op_array->arg_info[i].class_name_len + 1); + } + } + } + } + + if (op_array->brk_cont_array) { + zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont); + } + + if (op_array->static_variables) { + zend_hash_persist(op_array->static_variables, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + zend_accel_store(op_array->static_variables, sizeof(HashTable)); + } + + if (op_array->scope) { + op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope); + } + + if (op_array->doc_comment) { + if (ZCG(accel_directives).save_comments) { + zend_accel_store(op_array->doc_comment, op_array->doc_comment_len + 1); + } else { + if (!zend_shared_alloc_get_xlat_entry(op_array->doc_comment)) { + zend_shared_alloc_register_xlat_entry(op_array->doc_comment, op_array->doc_comment); + efree((char*)op_array->doc_comment); + } + op_array->doc_comment = NULL; + op_array->doc_comment_len = 0; + } + } + + if (op_array->try_catch_array) { + zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); + } + + if (op_array->vars) { + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars))) { + op_array->vars = (zend_compiled_variable*)persist_ptr; + } else { + int i; + zend_accel_store(op_array->vars, sizeof(zend_compiled_variable) * op_array->last_var); + for (i = 0; i < op_array->last_var; i++) { + zend_accel_store_interned_string(op_array->vars[i].name, op_array->vars[i].name_len + 1); + } + } + } + + /* "prototype" may be undefined if "scope" isn't set */ + if (op_array->scope && op_array->prototype) { + if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) { + op_array->prototype = (union _zend_function*)persist_ptr; + /* we use refcount to show that op_array is referenced from several places */ + op_array->prototype->op_array.refcount++; + } + } else { + op_array->prototype = NULL; + } +} + +static void zend_persist_op_array(zend_op_array *op_array TSRMLS_DC) +{ + zend_persist_op_array_ex(op_array, NULL TSRMLS_CC); +} + +static void zend_persist_property_info(zend_property_info *prop TSRMLS_DC) +{ + zend_accel_store_interned_string(prop->name, prop->name_length + 1); + if (prop->doc_comment) { + if (ZCG(accel_directives).save_comments) { + zend_accel_store(prop->doc_comment, prop->doc_comment_len + 1); + } else { + if (!zend_shared_alloc_get_xlat_entry(prop->doc_comment)) { + zend_shared_alloc_register_xlat_entry(prop->doc_comment, prop->doc_comment); + efree((char*)prop->doc_comment); + } + prop->doc_comment = NULL; + prop->doc_comment_len = 0; + } + } +} + +static void zend_persist_class_entry(zend_class_entry **pce TSRMLS_DC) +{ + zend_class_entry *ce = *pce; + + if (ce->type == ZEND_USER_CLASS) { + *pce = zend_accel_store(ce, sizeof(zend_class_entry)); + zend_accel_store_interned_string(ce->name, ce->name_length + 1); + zend_hash_persist(&ce->function_table, (zend_persist_func_t) zend_persist_op_array, sizeof(zend_op_array) TSRMLS_CC); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->default_properties_table) { + int i; + + zend_accel_store(ce->default_properties_table, sizeof(zval*) * ce->default_properties_count); + for (i = 0; i < ce->default_properties_count; i++) { + if (ce->default_properties_table[i]) { + zend_persist_zval_ptr(&ce->default_properties_table[i] TSRMLS_CC); + } + } + } + if (ce->default_static_members_table) { + int i; + + zend_accel_store(ce->default_static_members_table, sizeof(zval*) * ce->default_static_members_count); + for (i = 0; i < ce->default_static_members_count; i++) { + if (ce->default_static_members_table[i]) { + zend_persist_zval_ptr(&ce->default_static_members_table[i] TSRMLS_CC); + } + } + } + ce->static_members_table = NULL; +#else + zend_hash_persist(&ce->default_properties, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + zend_hash_persist(&ce->default_static_members, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + ce->static_members = NULL; +#endif + zend_hash_persist(&ce->constants_table, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC); + + if (ZEND_CE_FILENAME(ce)) { + /* do not free! PHP has centralized filename storage, compiler will free it */ + ZEND_CE_FILENAME(ce) = zend_accel_memdup(ZEND_CE_FILENAME(ce), strlen(ZEND_CE_FILENAME(ce)) + 1); + } + if (ZEND_CE_DOC_COMMENT(ce)) { + if (ZCG(accel_directives).save_comments) { + zend_accel_store(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce) + 1); + } else { + if (!zend_shared_alloc_get_xlat_entry(ZEND_CE_DOC_COMMENT(ce))) { + zend_shared_alloc_register_xlat_entry(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT(ce)); + efree((char*)ZEND_CE_DOC_COMMENT(ce)); + } + ZEND_CE_DOC_COMMENT(ce) = NULL; + ZEND_CE_DOC_COMMENT_LEN(ce) = 0; + } + } + zend_hash_persist(&ce->properties_info, (zend_persist_func_t) zend_persist_property_info, sizeof(zend_property_info) TSRMLS_CC); + if (ce->num_interfaces && ce->interfaces) { + efree(ce->interfaces); + } + ce->interfaces = NULL; /* will be filled in on fetch */ + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->num_traits && ce->traits) { + efree(ce->traits); + } + ce->traits = NULL; + + if (ce->trait_aliases) { + int i = 0; + while (ce->trait_aliases[i]) { + if (ce->trait_aliases[i]->trait_method) { + if (ce->trait_aliases[i]->trait_method->method_name) { + zend_accel_store(ce->trait_aliases[i]->trait_method->method_name, + ce->trait_aliases[i]->trait_method->mname_len + 1); + } + if (ce->trait_aliases[i]->trait_method->class_name) { + zend_accel_store(ce->trait_aliases[i]->trait_method->class_name, + ce->trait_aliases[i]->trait_method->cname_len + 1); + } + ce->trait_aliases[i]->trait_method->ce = NULL; + zend_accel_store(ce->trait_aliases[i]->trait_method, + sizeof(zend_trait_method_reference)); + } + + if (ce->trait_aliases[i]->alias) { + zend_accel_store(ce->trait_aliases[i]->alias, + ce->trait_aliases[i]->alias_len + 1); + } + +#if ZEND_EXTENSION_API_NO <= PHP_5_4_X_API_NO + ce->trait_aliases[i]->function = NULL; +#endif + zend_accel_store(ce->trait_aliases[i], sizeof(zend_trait_alias)); + i++; + } + + zend_accel_store(ce->trait_aliases, sizeof(zend_trait_alias*) * (i + 1)); + } + + if (ce->trait_precedences) { + int i = 0; + + while (ce->trait_precedences[i]) { + zend_accel_store(ce->trait_precedences[i]->trait_method->method_name, + ce->trait_precedences[i]->trait_method->mname_len + 1); + zend_accel_store(ce->trait_precedences[i]->trait_method->class_name, + ce->trait_precedences[i]->trait_method->cname_len + 1); + ce->trait_precedences[i]->trait_method->ce = NULL; + zend_accel_store(ce->trait_precedences[i]->trait_method, + sizeof(zend_trait_method_reference)); + + if (ce->trait_precedences[i]->exclude_from_classes) { + int j = 0; + + while (ce->trait_precedences[i]->exclude_from_classes[j]) { + zend_accel_store(ce->trait_precedences[i]->exclude_from_classes[j], + strlen((char*)ce->trait_precedences[i]->exclude_from_classes[j]) + 1); + j++; + } + zend_accel_store(ce->trait_precedences[i]->exclude_from_classes, + sizeof(zend_class_entry*) * (j + 1)); + } + +#if ZEND_EXTENSION_API_NO <= PHP_5_4_X_API_NO + ce->trait_precedences[i]->function = NULL; +#endif + zend_accel_store(ce->trait_precedences[i], sizeof(zend_trait_precedence)); + i++; + } + zend_accel_store( + ce->trait_precedences, sizeof(zend_trait_precedence*) * (i + 1)); + } +#endif + } +} + +static int zend_update_property_info_ce(zend_property_info *prop TSRMLS_DC) +{ + prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce); + return 0; +} + +static int zend_update_parent_ce(zend_class_entry **pce TSRMLS_DC) +{ + zend_class_entry *ce = *pce; + + if (ce->parent) { + ce->parent = zend_shared_alloc_get_xlat_entry(ce->parent); + /* We use refcount to show if the class is used as a parent */ + ce->parent->refcount++; + } + + /* update methods */ + if (ce->constructor) { + ce->constructor = zend_shared_alloc_get_xlat_entry(ce->constructor); + /* we use refcount to show that op_array is referenced from several places */ + ce->constructor->op_array.refcount++; + } + if (ce->destructor) { + ce->destructor = zend_shared_alloc_get_xlat_entry(ce->destructor); + ce->destructor->op_array.refcount++; + } + if (ce->clone) { + ce->clone = zend_shared_alloc_get_xlat_entry(ce->clone); + ce->clone->op_array.refcount++; + } + if (ce->__get) { + ce->__get = zend_shared_alloc_get_xlat_entry(ce->__get); + ce->__get->op_array.refcount++; + } + if (ce->__set) { + ce->__set = zend_shared_alloc_get_xlat_entry(ce->__set); + ce->__set->op_array.refcount++; + } + if (ce->__call) { + ce->__call = zend_shared_alloc_get_xlat_entry(ce->__call); + ce->__call->op_array.refcount++; + } + if (ce->serialize_func) { + ce->serialize_func = zend_shared_alloc_get_xlat_entry(ce->serialize_func); + ce->serialize_func->op_array.refcount++; + } + if (ce->unserialize_func) { + ce->unserialize_func = zend_shared_alloc_get_xlat_entry(ce->unserialize_func); + ce->unserialize_func->op_array.refcount++; + } + if (ce->__isset) { + ce->__isset = zend_shared_alloc_get_xlat_entry(ce->__isset); + ce->__isset->op_array.refcount++; + } + if (ce->__unset) { + ce->__unset = zend_shared_alloc_get_xlat_entry(ce->__unset); + ce->__unset->op_array.refcount++; + } + if (ce->__tostring) { + ce->__tostring = zend_shared_alloc_get_xlat_entry(ce->__tostring); + ce->__tostring->op_array.refcount++; + } +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + if (ce->__callstatic) { + ce->__callstatic = zend_shared_alloc_get_xlat_entry(ce->__callstatic); + ce->__callstatic->op_array.refcount++; + } +#endif + zend_hash_apply(&ce->properties_info, (apply_func_t) zend_update_property_info_ce TSRMLS_CC); + return 0; +} + +static void zend_accel_persist_class_table(HashTable *class_table TSRMLS_DC) +{ + zend_hash_persist(class_table, (zend_persist_func_t) zend_persist_class_entry, sizeof(zend_class_entry*) TSRMLS_CC); + zend_hash_apply(class_table, (apply_func_t) zend_update_parent_ce TSRMLS_CC); +} + +zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, char **key, unsigned int key_length TSRMLS_DC) +{ + zend_shared_alloc_clear_xlat_table(); + zend_hash_persist(&script->function_table, (zend_persist_func_t) zend_persist_op_array, sizeof(zend_op_array) TSRMLS_CC); + zend_accel_persist_class_table(&script->class_table TSRMLS_CC); + zend_persist_op_array_ex(&script->main_op_array, script TSRMLS_CC); + *key = zend_accel_memdup(*key, key_length + 1); + zend_accel_store(script->full_path, script->full_path_len + 1); + zend_accel_store(script, sizeof(zend_persistent_script)); + + return script; +} + +int zend_accel_script_persistable(zend_persistent_script *script) +{ + return 1; +} diff --git a/ext/opcache/zend_persist.h b/ext/opcache/zend_persist.h new file mode 100644 index 000000000..cf3fb73cd --- /dev/null +++ b/ext/opcache/zend_persist.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_PERSIST_H +#define ZEND_PERSIST_H + +int zend_accel_script_persistable(zend_persistent_script *script); +uint zend_accel_script_persist_calc(zend_persistent_script *script, char *key, unsigned int key_length TSRMLS_DC); +zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, char **key, unsigned int key_length TSRMLS_DC); + +#endif /* ZEND_PERSIST_H */ diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c new file mode 100644 index 000000000..18af756f6 --- /dev/null +++ b/ext/opcache/zend_persist_calc.c @@ -0,0 +1,343 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include "zend.h" +#include "ZendAccelerator.h" +#include "zend_persist.h" +#include "zend_extensions.h" +#include "zend_shared_alloc.h" +#include "zend_operators.h" + +#define START_SIZE() uint memory_used = 0 +#define ADD_DUP_SIZE(m,s) memory_used += zend_shared_memdup_size((void*)m, s) +#define ADD_SIZE(m) memory_used += ZEND_ALIGNED_SIZE(m) +#define RETURN_SIZE() return memory_used + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO +# define ADD_INTERNED_STRING(str, len) do { \ + const char *tmp = accel_new_interned_string((str), (len), !IS_INTERNED((str)) TSRMLS_CC); \ + if (tmp != (str)) { \ + (str) = (char*)tmp; \ + } else { \ + ADD_DUP_SIZE((str), (len)); \ + } \ + } while (0) +#else +# define ADD_INTERNED_STRING(str, len) ADD_DUP_SIZE((str), (len)) +#endif + +static uint zend_persist_zval_ptr_calc(zval **zp TSRMLS_DC); + +static uint zend_hash_persist_calc(HashTable *ht, int (*pPersistElement)(void *pElement TSRMLS_DC), size_t el_size TSRMLS_DC) +{ + Bucket *p = ht->pListHead; + START_SIZE(); + + while (p) { + /* persist bucket and key */ +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + ADD_DUP_SIZE(p, sizeof(Bucket)); + if (p->nKeyLength) { + const char *tmp = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC); + if (tmp != p->arKey) { + p->arKey = tmp; + } else { + ADD_DUP_SIZE(p->arKey, p->nKeyLength); + } + } +#else + ADD_DUP_SIZE(p, sizeof(Bucket) - 1 + p->nKeyLength); +#endif + + /* persist data pointer in bucket */ + if (!p->pDataPtr) { + ADD_DUP_SIZE(p->pData, el_size); + } + + /* persist the data itself */ + if (pPersistElement) { + ADD_SIZE(pPersistElement(p->pData TSRMLS_CC)); + } + + p = p->pListNext; + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ht->nTableMask) { + ADD_DUP_SIZE(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize); + } +#else + ADD_DUP_SIZE(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize); +#endif + + RETURN_SIZE(); +} + +static uint zend_persist_zval_calc(zval *z TSRMLS_DC) +{ + START_SIZE(); + +#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO + switch (z->type & IS_CONSTANT_TYPE_MASK) { +#else + switch (z->type & ~IS_CONSTANT_INDEX) { +#endif + case IS_STRING: + case IS_CONSTANT: + ADD_INTERNED_STRING(Z_STRVAL_P(z), Z_STRLEN_P(z) + 1); + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + ADD_DUP_SIZE(z->value.ht, sizeof(HashTable)); + ADD_SIZE(zend_hash_persist_calc(z->value.ht, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); + break; + } + RETURN_SIZE(); +} + +static uint zend_persist_zval_ptr_calc(zval **zp TSRMLS_DC) +{ + START_SIZE(); + zval *new_ptr = zend_shared_alloc_get_xlat_entry(*zp); + + if (!new_ptr) { + ADD_DUP_SIZE(*zp, sizeof(zval)); + ADD_SIZE(zend_persist_zval_calc(*zp TSRMLS_CC)); + } + RETURN_SIZE(); +} + +static uint zend_persist_op_array_calc(zend_op_array *op_array TSRMLS_DC) +{ + START_SIZE(); + + if (op_array->type != ZEND_USER_FUNCTION) { + return 0; + } + + if (op_array->filename) { + ADD_DUP_SIZE(op_array->filename, strlen(op_array->filename) + 1); + } + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (op_array->literals && !zend_shared_alloc_get_xlat_entry(op_array->literals)) { + zend_literal *p = op_array->literals; + zend_literal *end = p + op_array->last_literal; + ADD_DUP_SIZE(op_array->literals, sizeof(zend_literal) * op_array->last_literal); + while (p < end) { + ADD_SIZE(zend_persist_zval_calc(&p->constant TSRMLS_CC)); + p++; + } + } +#endif + + if (!zend_shared_alloc_get_xlat_entry(op_array->opcodes)) { +#if ZEND_EXTENSION_API_NO <= PHP_5_3_X_API_NO + zend_op *opline = op_array->opcodes; + zend_op *end = op_array->opcodes + op_array->last; + + ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last); + while (opline<end) { + if (opline->op1.op_type == IS_CONST) { + ADD_SIZE(zend_persist_zval_calc(&opline->op1.u.constant TSRMLS_CC)); + } + if (opline->op2.op_type == IS_CONST) { + ADD_SIZE(zend_persist_zval_calc(&opline->op2.u.constant TSRMLS_CC)); + } + opline++; + } +#else + ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last); +#endif + } + + if (op_array->function_name) { + ADD_DUP_SIZE(op_array->function_name, strlen(op_array->function_name) + 1); + } + + if (op_array->arg_info && + !zend_shared_alloc_get_xlat_entry(op_array->arg_info)) { + zend_uint i; + + ADD_DUP_SIZE(op_array->arg_info, sizeof(zend_arg_info) * op_array->num_args); + for (i = 0; i < op_array->num_args; i++) { + if (op_array->arg_info[i].name) { + ADD_INTERNED_STRING(op_array->arg_info[i].name, op_array->arg_info[i].name_len + 1); + } + if (op_array->arg_info[i].class_name) { + ADD_INTERNED_STRING(op_array->arg_info[i].class_name, op_array->arg_info[i].class_name_len + 1); + } + + } + } + + if (op_array->brk_cont_array) { + ADD_DUP_SIZE(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont); + } + + if (op_array->static_variables) { + ADD_DUP_SIZE(op_array->static_variables, sizeof(HashTable)); + ADD_SIZE(zend_hash_persist_calc(op_array->static_variables, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); + } + + if (ZCG(accel_directives).save_comments && op_array->doc_comment) { + ADD_DUP_SIZE(op_array->doc_comment, op_array->doc_comment_len + 1); + } + + if (op_array->try_catch_array) { + ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); + } + + if (op_array->vars && !zend_shared_alloc_get_xlat_entry(op_array->vars)) { + int i; + + ADD_DUP_SIZE(op_array->vars, sizeof(zend_compiled_variable) * op_array->last_var); + for (i = 0; i < op_array->last_var; i++) { + ADD_INTERNED_STRING(op_array->vars[i].name, op_array->vars[i].name_len + 1); + } + } + + RETURN_SIZE(); +} + +static uint zend_persist_property_info_calc(zend_property_info *prop TSRMLS_DC) +{ + START_SIZE(); + ADD_INTERNED_STRING(prop->name, prop->name_length + 1); + if (ZCG(accel_directives).save_comments && prop->doc_comment) { + ADD_DUP_SIZE(prop->doc_comment, prop->doc_comment_len + 1); + } + RETURN_SIZE(); +} + +static uint zend_persist_class_entry_calc(zend_class_entry **pce TSRMLS_DC) +{ + zend_class_entry *ce = *pce; + START_SIZE(); + + if (ce->type == ZEND_USER_CLASS) { + ADD_DUP_SIZE(ce, sizeof(zend_class_entry)); + ADD_INTERNED_STRING(ce->name, ce->name_length + 1); + ADD_SIZE(zend_hash_persist_calc(&ce->function_table, (int (*)(void* TSRMLS_DC)) zend_persist_op_array_calc, sizeof(zend_op_array) TSRMLS_CC)); +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->default_properties_table) { + int i; + + ADD_SIZE(sizeof(zval*) * ce->default_properties_count); + for (i = 0; i < ce->default_properties_count; i++) { + if (ce->default_properties_table[i]) { + ADD_SIZE(zend_persist_zval_ptr_calc(&ce->default_properties_table[i] TSRMLS_CC)); + } + } + } + if (ce->default_static_members_table) { + int i; + + ADD_SIZE(sizeof(zval*) * ce->default_static_members_count); + for (i = 0; i < ce->default_static_members_count; i++) { + if (ce->default_static_members_table[i]) { + ADD_SIZE(zend_persist_zval_ptr_calc(&ce->default_static_members_table[i] TSRMLS_CC)); + } + } + } +#else + ADD_SIZE(zend_hash_persist_calc(&ce->default_properties, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); + ADD_SIZE(zend_hash_persist_calc(&ce->default_static_members, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); +#endif + ADD_SIZE(zend_hash_persist_calc(&ce->constants_table, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC)); + + if (ZEND_CE_FILENAME(ce)) { + ADD_DUP_SIZE(ZEND_CE_FILENAME(ce), strlen(ZEND_CE_FILENAME(ce)) + 1); + } + if (ZCG(accel_directives).save_comments && ZEND_CE_DOC_COMMENT(ce)) { + ADD_DUP_SIZE(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce) + 1); + } + + ADD_SIZE(zend_hash_persist_calc(&ce->properties_info, (int (*)(void* TSRMLS_DC)) zend_persist_property_info_calc, sizeof(zend_property_info) TSRMLS_CC)); + +#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO + if (ce->trait_aliases) { + int i = 0; + while (ce->trait_aliases[i]) { + if (ce->trait_aliases[i]->trait_method) { + if (ce->trait_aliases[i]->trait_method->method_name) { + ADD_SIZE(ce->trait_aliases[i]->trait_method->mname_len + 1); + } + if (ce->trait_aliases[i]->trait_method->class_name) { + ADD_SIZE(ce->trait_aliases[i]->trait_method->cname_len + 1); + } + ADD_SIZE(sizeof(zend_trait_method_reference)); + } + + if (ce->trait_aliases[i]->alias) { + ADD_SIZE(ce->trait_aliases[i]->alias_len + 1); + } + ADD_SIZE(sizeof(zend_trait_alias)); + i++; + } + ADD_SIZE(sizeof(zend_trait_alias*) * (i + 1)); + } + + if (ce->trait_precedences) { + int i = 0; + + while (ce->trait_precedences[i]) { + ADD_SIZE(ce->trait_precedences[i]->trait_method->mname_len + 1); + ADD_SIZE(ce->trait_precedences[i]->trait_method->cname_len + 1); + ADD_SIZE(sizeof(zend_trait_method_reference)); + + if (ce->trait_precedences[i]->exclude_from_classes) { + int j = 0; + + while (ce->trait_precedences[i]->exclude_from_classes[j]) { + ADD_SIZE(strlen((char*)ce->trait_precedences[i]->exclude_from_classes[j]) + 1); + j++; + } + ADD_SIZE(sizeof(zend_class_entry*) * (j + 1)); + } + ADD_SIZE(sizeof(zend_trait_precedence)); + i++; + } + ADD_SIZE(sizeof(zend_trait_precedence*) * (i + 1)); + } +#endif + } + RETURN_SIZE(); +} + +static uint zend_accel_persist_class_table_calc(HashTable *class_table TSRMLS_DC) +{ + return zend_hash_persist_calc(class_table, (int (*)(void* TSRMLS_DC)) zend_persist_class_entry_calc, sizeof(zend_class_entry*) TSRMLS_CC); +} + +uint zend_accel_script_persist_calc(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length TSRMLS_DC) +{ + START_SIZE(); + + ADD_SIZE(zend_hash_persist_calc(&new_persistent_script->function_table, (int (*)(void* TSRMLS_DC)) zend_persist_op_array_calc, sizeof(zend_op_array) TSRMLS_CC)); + ADD_SIZE(zend_accel_persist_class_table_calc(&new_persistent_script->class_table TSRMLS_CC)); + ADD_SIZE(zend_persist_op_array_calc(&new_persistent_script->main_op_array TSRMLS_CC)); + ADD_DUP_SIZE(key, key_length + 1); + ADD_DUP_SIZE(new_persistent_script->full_path, new_persistent_script->full_path_len + 1); + ADD_DUP_SIZE(new_persistent_script, sizeof(zend_persistent_script)); + + RETURN_SIZE(); +} diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c new file mode 100644 index 000000000..f4465ce78 --- /dev/null +++ b/ext/opcache/zend_shared_alloc.c @@ -0,0 +1,489 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#include <errno.h> +#include "ZendAccelerator.h" +#include "zend_shared_alloc.h" +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#include <fcntl.h> +#ifndef ZEND_WIN32 +# include <sys/types.h> +# include <dirent.h> +# include <signal.h> +# include <sys/stat.h> +# include <stdio.h> +#endif + +#ifdef HAVE_MPROTECT +# include "sys/mman.h" +#endif + +#define TMP_DIR "/tmp" +#define SEM_FILENAME_PREFIX ".ZendSem." +#define S_H(s) g_shared_alloc_handler->s + +/* True globals */ +/* old/new mapping. We can use true global even for ZTS because its usage + is wrapped with exclusive lock anyway */ +static HashTable xlat_table; +static const zend_shared_memory_handlers *g_shared_alloc_handler = NULL; +static const char *g_shared_model; +/* pointer to globals allocated in SHM and shared across processes */ +zend_smm_shared_globals *smm_shared_globals; + +#ifndef ZEND_WIN32 +#ifdef ZTS +static MUTEX_T zts_lock; +#endif +int lock_file; +static char lockfile_name[sizeof(TMP_DIR) + sizeof(SEM_FILENAME_PREFIX) + 8]; +#endif + +static const zend_shared_memory_handler_entry handler_table[] = { +#ifdef USE_MMAP + { "mmap", &zend_alloc_mmap_handlers }, +#endif +#ifdef USE_SHM + { "shm", &zend_alloc_shm_handlers }, +#endif +#ifdef USE_SHM_OPEN + { "posix", &zend_alloc_posix_handlers }, +#endif +#ifdef ZEND_WIN32 + { "win32", &zend_alloc_win32_handlers }, +#endif + { NULL, NULL} +}; + +#ifndef ZEND_WIN32 +void zend_shared_alloc_create_lock(void) +{ + int val; + +#ifdef ZTS + zts_lock = tsrm_mutex_alloc(); +#endif + + sprintf(lockfile_name, "%s/%sXXXXXX", TMP_DIR, SEM_FILENAME_PREFIX); + lock_file = mkstemp(lockfile_name); + fchmod(lock_file, 0666); + + if (lock_file == -1) { + zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno); + } + val = fcntl(lock_file, F_GETFD, 0); + val |= FD_CLOEXEC; + fcntl(lock_file, F_SETFD, val); + + unlink(lockfile_name); +} +#endif + +static void no_memory_bailout(int allocate_size, char *error) +{ + zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %d bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno ); +} + +static void copy_shared_segments(void *to, void *from, int count, int size) +{ + zend_shared_segment **shared_segments_v = (zend_shared_segment **)to; + void *shared_segments_to_p = ((char *)to + count*(sizeof(void *))); + void *shared_segments_from_p = from; + int i; + + for (i = 0; i < count; i++) { + shared_segments_v[i] = shared_segments_to_p; + memcpy(shared_segments_to_p, shared_segments_from_p, size); + shared_segments_to_p = ((char *)shared_segments_to_p + size); + shared_segments_from_p = ((char *)shared_segments_from_p + size); + } +} + +static int zend_shared_alloc_try(const zend_shared_memory_handler_entry *he, int requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in) +{ + int res; + g_shared_alloc_handler = he->handler; + g_shared_model = he->name; + ZSMMG(shared_segments) = NULL; + ZSMMG(shared_segments_count) = 0; + + res = S_H(create_segments)(requested_size, shared_segments_p, shared_segments_count, error_in); + + if (res) { + /* this model works! */ + return res; + } + if (*shared_segments_p) { + int i; + /* cleanup */ + for (i = 0; i < *shared_segments_count; i++) { + if ((*shared_segments_p)[i]->p && (int)(*shared_segments_p)[i]->p != -1) { + S_H(detach_segment)((*shared_segments_p)[i]); + } + } + free(*shared_segments_p); + *shared_segments_p = NULL; + } + g_shared_alloc_handler = NULL; + return ALLOC_FAILURE; +} + +int zend_shared_alloc_startup(int requested_size) +{ + zend_shared_segment **tmp_shared_segments; + size_t shared_segments_array_size; + zend_smm_shared_globals tmp_shared_globals, *p_tmp_shared_globals; + char *error_in = NULL; + const zend_shared_memory_handler_entry *he; + int res = ALLOC_FAILURE; + + TSRMLS_FETCH(); + + /* shared_free must be valid before we call zend_shared_alloc() + * - make it temporarily point to a local variable + */ + smm_shared_globals = &tmp_shared_globals; + ZSMMG(shared_free) = requested_size; /* goes to tmp_shared_globals.shared_free */ + + zend_shared_alloc_create_lock(); + + if (ZCG(accel_directives).memory_model && ZCG(accel_directives).memory_model[0]) { + char *model = ZCG(accel_directives).memory_model; + /* "cgi" is really "shm"... */ + if (strncmp(ZCG(accel_directives).memory_model, "cgi", sizeof("cgi")) == 0) { + model = "shm"; + } + + for (he = handler_table; he->name; he++) { + if (strcmp(model, he->name) == 0) { + res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in); + if (res) { + /* this model works! */ + } + break; + } + } + } + + if (res == FAILED_REATTACHED) { + smm_shared_globals = NULL; + return res; + } + + if (!g_shared_alloc_handler) { + /* try memory handlers in order */ + for (he = handler_table; he->name; he++) { + res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in); + if (res) { + /* this model works! */ + break; + } + } + } + + if (!g_shared_alloc_handler) { + no_memory_bailout(requested_size, error_in); + return ALLOC_FAILURE; + } + + if (res == SUCCESSFULLY_REATTACHED) { + return res; + } + + shared_segments_array_size = ZSMMG(shared_segments_count) * S_H(segment_type_size)(); + + /* move shared_segments and shared_free to shared memory */ + ZCG(locked) = 1; /* no need to perform a real lock at this point */ + p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals)); + + tmp_shared_segments = zend_shared_alloc(shared_segments_array_size + ZSMMG(shared_segments_count) * sizeof(void *)); + copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)()); + + *p_tmp_shared_globals = tmp_shared_globals; + smm_shared_globals = p_tmp_shared_globals; + + free(ZSMMG(shared_segments)); + ZSMMG(shared_segments) = tmp_shared_segments; + + ZSMMG(shared_memory_state).positions = (int *)zend_shared_alloc(sizeof(int) * ZSMMG(shared_segments_count)); + ZCG(locked) = 0; + + return res; +} + +void zend_shared_alloc_shutdown(void) +{ + zend_shared_segment **tmp_shared_segments; + size_t shared_segments_array_size; + zend_smm_shared_globals tmp_shared_globals; + int i; + + tmp_shared_globals = *smm_shared_globals; + smm_shared_globals = &tmp_shared_globals; + shared_segments_array_size = ZSMMG(shared_segments_count) * (S_H(segment_type_size)() + sizeof(void *)); + tmp_shared_segments = emalloc(shared_segments_array_size); + copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)()); + ZSMMG(shared_segments) = tmp_shared_segments; + + for (i = 0; i < ZSMMG(shared_segments_count); i++) { + S_H(detach_segment)(ZSMMG(shared_segments)[i]); + } + efree(ZSMMG(shared_segments)); + ZSMMG(shared_segments) = NULL; + g_shared_alloc_handler = NULL; +#ifndef ZEND_WIN32 + close(lock_file); +#endif +} + +static size_t zend_shared_alloc_get_largest_free_block(void) +{ + int i; + size_t largest_block_size = 0; + + for (i = 0; i < ZSMMG(shared_segments_count); i++) { + size_t block_size = ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos; + + if (block_size>largest_block_size) { + largest_block_size = block_size; + } + } + return largest_block_size; +} + +#define MIN_FREE_MEMORY 64*1024 + +#define SHARED_ALLOC_FAILED() do { \ + zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate %ld bytes (%ld bytes free)", (long)size, (long)ZSMMG(shared_free)); \ + if (zend_shared_alloc_get_largest_free_block() < MIN_FREE_MEMORY) { \ + ZSMMG(memory_exhausted) = 1; \ + zend_accel_schedule_restart(ACCEL_RESTART_OOM TSRMLS_CC); \ + } \ + } while (0) + +void *zend_shared_alloc(size_t size) +{ + int i; + unsigned int block_size = size + sizeof(zend_shared_memory_block_header); + TSRMLS_FETCH(); + +#if 1 + if (!ZCG(locked)) { + zend_accel_error(ACCEL_LOG_ERROR, "Shared memory lock not obtained"); + } +#endif + if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */ + SHARED_ALLOC_FAILED(); + return NULL; + } + for (i = 0; i < ZSMMG(shared_segments_count); i++) { + if (ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos >= block_size) { /* found a valid block */ + zend_shared_memory_block_header *p = (zend_shared_memory_block_header *) (((char *) ZSMMG(shared_segments)[i]->p) + ZSMMG(shared_segments)[i]->pos); + int remainder = block_size % PLATFORM_ALIGNMENT; + void *retval; + + if (remainder != 0) { + size += PLATFORM_ALIGNMENT - remainder; + block_size += PLATFORM_ALIGNMENT - remainder; + } + ZSMMG(shared_segments)[i]->pos += block_size; + ZSMMG(shared_free) -= block_size; + p->size = size; + retval = ((char *) p) + sizeof(zend_shared_memory_block_header); + memset(retval, 0, size); + return retval; + } + } + SHARED_ALLOC_FAILED(); + return NULL; +} + +int zend_shared_memdup_size(void *source, size_t size) +{ + void **old_p; + + if (zend_hash_index_find(&xlat_table, (ulong)source, (void **)&old_p) == SUCCESS) { + /* we already duplicated this pointer */ + return 0; + } + zend_shared_alloc_register_xlat_entry(source, source); + return ZEND_ALIGNED_SIZE(size); +} + +void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source TSRMLS_DC) +{ + void **old_p, *retval; + + if (zend_hash_index_find(&xlat_table, (ulong)source, (void **)&old_p) == SUCCESS) { + /* we already duplicated this pointer */ + return *old_p; + } + retval = ZCG(mem);; + ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size)); + memcpy(retval, source, size); + if (free_source) { + interned_efree((char*)source); + } + zend_shared_alloc_register_xlat_entry(source, retval); + return retval; +} + +void zend_shared_alloc_safe_unlock(TSRMLS_D) +{ + if (ZCG(locked)) { + zend_shared_alloc_unlock(TSRMLS_C); + } +} + +#ifndef ZEND_WIN32 +/* name l_type l_whence l_start l_len */ +static FLOCK_STRUCTURE(mem_write_lock, F_WRLCK, SEEK_SET, 0, 1); +static FLOCK_STRUCTURE(mem_write_unlock, F_UNLCK, SEEK_SET, 0, 1); +#endif + +void zend_shared_alloc_lock(TSRMLS_D) +{ +#ifndef ZEND_WIN32 + +#ifdef ZTS + tsrm_mutex_lock(zts_lock); +#endif + +#if 0 + /* this will happen once per process, and will un-globalize mem_write_lock */ + if (mem_write_lock.l_pid == -1) { + mem_write_lock.l_pid = getpid(); + } +#endif + + while (1) { + if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) { + if (errno == EINTR) { + continue; + } + zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno); + } + break; + } +#else + zend_shared_alloc_lock_win32(); +#endif + + ZCG(locked) = 1; + + /* Prepare translation table + * + * Make it persistent so that it uses malloc() and allocated blocks + * won't be taken from space which is freed by efree in memdup. + * Otherwise it leads to false matches in memdup check. + */ + zend_hash_init(&xlat_table, 100, NULL, NULL, 1); +} + +void zend_shared_alloc_unlock(TSRMLS_D) +{ + /* Destroy translation table */ + zend_hash_destroy(&xlat_table); + + ZCG(locked) = 0; + +#ifndef ZEND_WIN32 + if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) { + zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno); + } +#ifdef ZTS + tsrm_mutex_unlock(zts_lock); +#endif +#else + zend_shared_alloc_unlock_win32(); +#endif +} + +void zend_shared_alloc_clear_xlat_table(void) +{ + zend_hash_clean(&xlat_table); +} + +void zend_shared_alloc_register_xlat_entry(const void *old, const void *new) +{ + zend_hash_index_update(&xlat_table, (ulong)old, (void*)&new, sizeof(void *), NULL); +} + +void *zend_shared_alloc_get_xlat_entry(const void *old) +{ + void **retval; + + if (zend_hash_index_find(&xlat_table, (ulong)old, (void **)&retval) == FAILURE) { + return NULL; + } + return *retval; +} + +size_t zend_shared_alloc_get_free_memory(void) +{ + return ZSMMG(shared_free); +} + +void zend_shared_alloc_save_state(void) +{ + int i; + + for (i = 0; i < ZSMMG(shared_segments_count); i++) { + ZSMMG(shared_memory_state).positions[i] = ZSMMG(shared_segments)[i]->pos; + } + ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free); +} + +void zend_shared_alloc_restore_state(void) +{ + int i; + + for (i = 0; i < ZSMMG(shared_segments_count); i++) { + ZSMMG(shared_segments)[i]->pos = ZSMMG(shared_memory_state).positions[i]; + } + ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free; + ZSMMG(memory_exhausted) = 0; + ZSMMG(wasted_shared_memory) = 0; +} + +const char *zend_accel_get_shared_model(void) +{ + return g_shared_model; +} + +void zend_accel_shared_protect(int mode TSRMLS_DC) +{ +#ifdef HAVE_MPROTECT + int i; + + if (mode) { + mode = PROT_READ; + } else { + mode = PROT_READ|PROT_WRITE; + } + + for (i = 0; i < ZSMMG(shared_segments_count); i++) { + mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode); + } +#endif +} diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h new file mode 100644 index 000000000..23af630f8 --- /dev/null +++ b/ext/opcache/zend_shared_alloc.h @@ -0,0 +1,186 @@ +/* + +----------------------------------------------------------------------+ + | Zend OPcache | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Andi Gutmans <andi@zend.com> | + | Zeev Suraski <zeev@zend.com> | + | Stanislav Malyshev <stas@zend.com> | + | Dmitry Stogov <dmitry@zend.com> | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_SHARED_ALLOC_H +#define ZEND_SHARED_ALLOC_H + +#include "zend.h" +#include "ZendAccelerator.h" + +#if defined(__APPLE__) && defined(__MACH__) /* darwin */ +# ifdef HAVE_SHM_MMAP_POSIX +# define USE_SHM_OPEN 1 +# endif +# ifdef HAVE_SHM_MMAP_ANON +# define USE_MMAP 1 +# endif +#elif defined(__linux__) || defined(_AIX) +# ifdef HAVE_SHM_IPC +# define USE_SHM 1 +# endif +# ifdef HAVE_SHM_MMAP_ANON +# define USE_MMAP 1 +# endif +#elif defined(__sparc) || defined(__sun) +# ifdef HAVE_SHM_MMAP_POSIX +# define USE_SHM_OPEN 1 +# endif +# ifdef HAVE_SHM_IPC +# define USE_SHM 1 +# endif +# if defined(__i386) +# ifdef HAVE_SHM_MMAP_ANON +# define USE_MMAP 1 +# endif +# endif +#else +# ifdef HAVE_SHM_MMAP_POSIX +# define USE_SHM_OPEN 1 +# endif +# ifdef HAVE_SHM_MMAP_ANON +# define USE_MMAP 1 +# endif +# ifdef HAVE_SHM_IPC +# define USE_SHM 1 +# endif +#endif + +#define ALLOC_FAILURE 0 +#define ALLOC_SUCCESS 1 +#define FAILED_REATTACHED 2 +#define SUCCESSFULLY_REATTACHED 4 +#define ALLOC_FAIL_MAPPING 8 + +typedef struct _zend_shared_segment { + size_t size; + size_t pos; /* position for simple stack allocator */ + void *p; +} zend_shared_segment; + +typedef int (*create_segments_t)(size_t requested_size, zend_shared_segment ***shared_segments, int *shared_segment_count, char **error_in); +typedef int (*detach_segment_t)(zend_shared_segment *shared_segment); + +typedef struct { + create_segments_t create_segments; + detach_segment_t detach_segment; + size_t (*segment_type_size)(void); +} zend_shared_memory_handlers; + +typedef struct _handler_entry { + const char *name; + zend_shared_memory_handlers *handler; +} zend_shared_memory_handler_entry; + +typedef struct _zend_shared_memory_block_header { + int size; +} zend_shared_memory_block_header; + +typedef struct _zend_shared_memory_state { + int *positions; /* current positions for each segment */ + int shared_free; /* amount of free shared memory */ +} zend_shared_memory_state; + +typedef struct _zend_smm_shared_globals { + /* Shared Memory Manager */ + zend_shared_segment **shared_segments; + /* Number of allocated shared segments */ + int shared_segments_count; + /* Amount of free shared memory */ + size_t shared_free; + /* Amount of shared memory allocated by garbage */ + int wasted_shared_memory; + /* No more shared memory flag */ + zend_bool memory_exhausted; + /* Saved Shared Allocator State */ + zend_shared_memory_state shared_memory_state; + /* Pointer to the application's shared data structures */ + void *app_shared_globals; +} zend_smm_shared_globals; + +extern zend_smm_shared_globals *smm_shared_globals; + +#define ZSMMG(element) (smm_shared_globals->element) + +#define SHARED_ALLOC_REATTACHED (SUCCESS+1) + +int zend_shared_alloc_startup(int requested_size); +void zend_shared_alloc_shutdown(void); + +/* allocate shared memory block */ +void *zend_shared_alloc(size_t size); + +/* copy into shared memory */ +void *_zend_shared_memdup(void *p, size_t size, zend_bool free_source TSRMLS_DC); +int zend_shared_memdup_size(void *p, size_t size); + +typedef union _align_test { + void *ptr; + double dbl; + long lng; +} align_test; + +#if ZEND_GCC_VERSION >= 2000 +# define PLATFORM_ALIGNMENT (__alignof__ (align_test)) +#else +# define PLATFORM_ALIGNMENT (sizeof(align_test)) +#endif + +#define ZEND_ALIGNED_SIZE(size) \ + ((size + PLATFORM_ALIGNMENT - 1) & ~(PLATFORM_ALIGNMENT - 1)) + +/* exclusive locking */ +void zend_shared_alloc_lock(TSRMLS_D); +void zend_shared_alloc_unlock(TSRMLS_D); /* returns the allocated size during lock..unlock */ +void zend_shared_alloc_safe_unlock(TSRMLS_D); + +/* old/new mapping functions */ +void zend_shared_alloc_clear_xlat_table(void); +void zend_shared_alloc_register_xlat_entry(const void *old, const void *new); +void *zend_shared_alloc_get_xlat_entry(const void *old); + +size_t zend_shared_alloc_get_free_memory(void); +void zend_shared_alloc_save_state(void); +void zend_shared_alloc_restore_state(void); +const char *zend_accel_get_shared_model(void); + +/* memory write protection */ +void zend_accel_shared_protect(int mode TSRMLS_DC); + +#ifdef USE_MMAP +extern zend_shared_memory_handlers zend_alloc_mmap_handlers; +#endif + +#ifdef USE_SHM +extern zend_shared_memory_handlers zend_alloc_shm_handlers; +#endif + +#ifdef USE_SHM_OPEN +extern zend_shared_memory_handlers zend_alloc_posix_handlers; +#endif + +#ifdef ZEND_WIN32 +extern zend_shared_memory_handlers zend_alloc_win32_handlers; +void zend_shared_alloc_create_lock(void); +void zend_shared_alloc_lock_win32(void); +void zend_shared_alloc_unlock_win32(void); +#endif + +#endif /* ZEND_SHARED_ALLOC_H */ diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index b1ee89a8a..7d34d9feb 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -251,7 +251,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_le char *tmp = NULL; #if HAVE_SETLOCALE -# ifdef PHP_WIN32 && ZTS +# if defined(PHP_WIN32) && defined(ZTS) _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); # endif locale = setlocale(LC_CTYPE, NULL); diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 5dc445ff8..1b0db91c3 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2499,16 +2499,15 @@ static void pdo_stmt_iter_get_data(zend_object_iterator *iter, zval ***data TSRM *data = &I->fetch_ahead; } -static int pdo_stmt_iter_get_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, - ulong *int_key TSRMLS_DC) +static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key TSRMLS_DC) { struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter->data; if (I->key == (ulong)-1) { - return HASH_KEY_NON_EXISTANT; + ZVAL_NULL(key); + } else { + ZVAL_LONG(key, I->key); } - *int_key = I->key; - return HASH_KEY_IS_LONG; } static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter TSRMLS_DC) diff --git a/ext/pdo_mysql/mysql_statement.c b/ext/pdo_mysql/mysql_statement.c index f2e36c171..2ae559571 100644 --- a/ext/pdo_mysql/mysql_statement.c +++ b/ext/pdo_mysql/mysql_statement.c @@ -343,7 +343,6 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ pdo_mysql_stmt *S = (pdo_mysql_stmt*)stmt->driver_data; pdo_mysql_db_handle *H = S->H; long row_count; - int ret; PDO_DBG_ENTER("pdo_mysql_stmt_next_rowset"); PDO_DBG_INF_FMT("stmt=%p", S->stmt); @@ -412,26 +411,21 @@ static int pdo_mysql_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ S->result = NULL; } - ret = mysql_next_result(H->server); + if (!mysql_more_results(H->server)) { + /* No more results */ + PDO_DBG_RETURN(0); + } #if PDO_USE_MYSQLND - /* for whatever reason mysqlnd breaks with libmysql compatibility at the C level, no -1 */ - if (PASS != ret) { + if (mysql_next_result(H->server) == FAIL) { pdo_mysql_error_stmt(stmt); PDO_DBG_RETURN(0); - } - if (mysql_more_results(H->server)) { - PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt TSRMLS_CC)); } else { - /* No more results */ - PDO_DBG_RETURN(0); + PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt TSRMLS_CC)); } #else - if (ret > 0) { + if (mysql_next_result(H->server) > 0) { pdo_mysql_error_stmt(stmt); PDO_DBG_RETURN(0); - } else if (ret < 0) { - /* No more results */ - PDO_DBG_RETURN(0); } else { PDO_DBG_RETURN(pdo_mysql_fill_stmt_from_result(stmt TSRMLS_CC)); } diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index a6dd2c814..94ffd3744 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1434,16 +1434,13 @@ struct _phar_t { static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */ { zval **value; - zend_uchar key_type; zend_bool close_fp = 1; - ulong int_key; struct _phar_t *p_obj = (struct _phar_t*) puser; uint str_key_len, base_len = p_obj->l, fname_len; phar_entry_data *data; php_stream *fp; size_t contents_len; char *fname, *error = NULL, *base = p_obj->b, *opened, *save = NULL, *temp = NULL; - phar_zstr key; char *str_key; zend_class_entry *ce = p_obj->c; phar_archive_object *phar_obj = p_obj->p; @@ -1478,35 +1475,24 @@ static int phar_build(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ } if (iter->funcs->get_current_key) { - key_type = iter->funcs->get_current_key(iter, &key, &str_key_len, &int_key TSRMLS_CC); + zval key; + iter->funcs->get_current_key(iter, &key TSRMLS_CC); if (EG(exception)) { return ZEND_HASH_APPLY_STOP; } - if (key_type == HASH_KEY_IS_LONG) { + if (Z_TYPE(key) != IS_STRING) { + zval_dtor(&key); zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } - if (key_type > 9) { /* IS_UNICODE == 10 */ -#if PHP_VERSION_ID < 60000 -/* this can never happen, but fixes a compile warning */ - spprintf(&str_key, 0, "%s", key); -#else - spprintf(&str_key, 0, "%v", key); - ezfree(key); -#endif - } else { - PHAR_STR(key, str_key); - } + str_key_len = Z_STRLEN(key); + str_key = estrndup(Z_STRVAL(key), str_key_len); save = str_key; - - if (str_key[str_key_len - 1] == '\0') { - str_key_len--; - } - + zval_dtor(&key); } else { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; @@ -1641,32 +1627,24 @@ phar_spl_fileinfo: } } else { if (iter->funcs->get_current_key) { - key_type = iter->funcs->get_current_key(iter, &key, &str_key_len, &int_key TSRMLS_CC); + zval key; + iter->funcs->get_current_key(iter, &key TSRMLS_CC); if (EG(exception)) { return ZEND_HASH_APPLY_STOP; } - if (key_type == HASH_KEY_IS_LONG) { + if (Z_TYPE(key) != IS_STRING) { + zval_dtor(&key); zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; } - if (key_type > 9) { /* IS_UNICODE == 10 */ -#if PHP_VERSION_ID < 60000 -/* this can never happen, but fixes a compile warning */ - spprintf(&str_key, 0, "%s", key); -#else - spprintf(&str_key, 0, "%v", key); - ezfree(key); -#endif - } else { - PHAR_STR(key, str_key); - } + str_key_len = Z_STRLEN(key); + str_key = estrndup(Z_STRVAL(key), str_key_len); save = str_key; - - if (str_key[str_key_len - 1] == '\0') str_key_len--; + zval_dtor(&key); } else { zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Iterator %v returned an invalid key (must return a string)", ce->name); return ZEND_HASH_APPLY_STOP; diff --git a/ext/phar/tests/create_new_and_modify.phpt b/ext/phar/tests/create_new_and_modify.phpt index d6c469d9c..c03576cb2 100644 --- a/ext/phar/tests/create_new_and_modify.phpt +++ b/ext/phar/tests/create_new_and_modify.phpt @@ -21,6 +21,14 @@ $sig1 = $phar->getSignature(); include $pname . '/a.php'; +if (function_exists("opcache_get_status")) { + $status = opcache_get_status(); + if ($status["opcache_enabled"]) { + ini_set("opcache.revalidate_freq", "0"); + sleep(2); + } +} + file_put_contents($pname .'/a.php', "modified!\n"); file_put_contents($pname .'/b.php', "another!\n"); diff --git a/ext/phar/tests/delete_in_phar.phpt b/ext/phar/tests/delete_in_phar.phpt index 4842d2791..d28f136f7 100644 --- a/ext/phar/tests/delete_in_phar.phpt +++ b/ext/phar/tests/delete_in_phar.phpt @@ -15,6 +15,15 @@ $files = array(); $files['a.php'] = '<?php echo "This is a\n"; ?>'; $files['b.php'] = '<?php echo "This is b\n"; ?>'; $files['b/c.php'] = '<?php echo "This is b/c\n"; ?>'; + +if (function_exists("opcache_get_status")) { + $status = opcache_get_status(); + if ($status["opcache_enabled"]) { + ini_set("opcache.revalidate_freq", "0"); + sleep(2); + } +} + include 'files/phar_test.inc'; include $pname . '/a.php'; diff --git a/ext/phar/tests/delete_in_phar_confirm.phpt b/ext/phar/tests/delete_in_phar_confirm.phpt index 13a8d0db2..0d4eb1e2a 100644 --- a/ext/phar/tests/delete_in_phar_confirm.phpt +++ b/ext/phar/tests/delete_in_phar_confirm.phpt @@ -15,6 +15,15 @@ $files = array(); $files['a.php'] = '<?php echo "This is a\n"; ?>'; $files['b.php'] = '<?php echo "This is b\n"; ?>'; $files['b/c.php'] = '<?php echo "This is b/c\n"; ?>'; + +if (function_exists("opcache_get_status")) { + $status = opcache_get_status(); + if ($status["opcache_enabled"]) { + ini_set("opcache.revalidate_freq", "0"); + sleep(2); + } +} + include 'files/phar_test.inc'; include $pname . '/a.php'; diff --git a/ext/phar/tests/tar/create_new_and_modify.phpt b/ext/phar/tests/tar/create_new_and_modify.phpt index 8062fda76..905bfabc8 100644 --- a/ext/phar/tests/tar/create_new_and_modify.phpt +++ b/ext/phar/tests/tar/create_new_and_modify.phpt @@ -15,6 +15,14 @@ $pname = 'phar://' . $fname; file_put_contents($pname . '/a.php', "brand new!\n"); +if (function_exists("opcache_get_status")) { + $status = opcache_get_status(); + if ($status["opcache_enabled"]) { + ini_set("opcache.revalidate_freq", "0"); + sleep(2); + } +} + $phar = new Phar($fname); var_dump($phar->isFileFormat(Phar::TAR)); $sig1 = md5_file($fname); diff --git a/ext/phar/tests/tar/delete_in_phar.phpt b/ext/phar/tests/tar/delete_in_phar.phpt index 91ef4a204..1982b6bda 100644 --- a/ext/phar/tests/tar/delete_in_phar.phpt +++ b/ext/phar/tests/tar/delete_in_phar.phpt @@ -18,6 +18,14 @@ $phar['b/c.php'] = '<?php echo "This is b/c\n"; ?>'; $phar->setStub('<?php __HALT_COMPILER(); ?>'); $phar->stopBuffering(); +if (function_exists("opcache_get_status")) { + $status = opcache_get_status(); + if ($status["opcache_enabled"]) { + ini_set("opcache.revalidate_freq", "0"); + sleep(2); + } +} + include $alias . '/a.php'; include $alias . '/b.php'; include $alias . '/b/c.php'; diff --git a/ext/phar/tests/tar/delete_in_phar_confirm.phpt b/ext/phar/tests/tar/delete_in_phar_confirm.phpt index 707bcbd0e..7593ebc1f 100644 --- a/ext/phar/tests/tar/delete_in_phar_confirm.phpt +++ b/ext/phar/tests/tar/delete_in_phar_confirm.phpt @@ -18,6 +18,14 @@ $phar['b/c.php'] = '<?php echo "This is b/c\n"; ?>'; $phar->setStub('<?php __HALT_COMPILER(); ?>'); $phar->stopBuffering(); +if (function_exists("opcache_get_status")) { + $status = opcache_get_status(); + if ($status["opcache_enabled"]) { + ini_set("opcache.revalidate_freq", "0"); + sleep(2); + } +} + include $alias . '/a.php'; include $alias . '/b.php'; include $alias . '/b/c.php'; diff --git a/ext/phar/tests/zip/create_new_and_modify.phpt b/ext/phar/tests/zip/create_new_and_modify.phpt index 5a3ec3317..55d69cca0 100644 --- a/ext/phar/tests/zip/create_new_and_modify.phpt +++ b/ext/phar/tests/zip/create_new_and_modify.phpt @@ -15,6 +15,14 @@ $pname = 'phar://' . $fname; file_put_contents($pname . '/a.php', "brand new!\n"); +if (function_exists("opcache_get_status")) { + $status = opcache_get_status(); + if ($status["opcache_enabled"]) { + ini_set("opcache.revalidate_freq", "0"); + sleep(2); + } +} + $phar = new Phar($fname); var_dump($phar->isFileFormat(Phar::ZIP)); $sig1 = md5_file($fname); diff --git a/ext/phar/tests/zip/delete_in_phar.phpt b/ext/phar/tests/zip/delete_in_phar.phpt index b7bda7ca4..f01280013 100644 --- a/ext/phar/tests/zip/delete_in_phar.phpt +++ b/ext/phar/tests/zip/delete_in_phar.phpt @@ -18,6 +18,14 @@ $phar['b/c.php'] = '<?php echo "This is b/c\n"; ?>'; $phar->setStub('<?php __HALT_COMPILER(); ?>'); $phar->stopBuffering(); +if (function_exists("opcache_get_status")) { + $status = opcache_get_status(); + if ($status["opcache_enabled"]) { + ini_set("opcache.revalidate_freq", "0"); + sleep(2); + } +} + include $alias . '/a.php'; include $alias . '/b.php'; include $alias . '/b/c.php'; diff --git a/ext/phar/tests/zip/delete_in_phar_confirm.phpt b/ext/phar/tests/zip/delete_in_phar_confirm.phpt index fdd0b42b5..008049917 100644 --- a/ext/phar/tests/zip/delete_in_phar_confirm.phpt +++ b/ext/phar/tests/zip/delete_in_phar_confirm.phpt @@ -18,6 +18,14 @@ $phar['b/c.php'] = '<?php echo "This is b/c\n"; ?>'; $phar->setStub('<?php __HALT_COMPILER(); ?>'); $phar->stopBuffering(); +if (function_exists("opcache_get_status")) { + $status = opcache_get_status(); + if ($status["opcache_enabled"]) { + ini_set("opcache.revalidate_freq", "0"); + sleep(2); + } +} + include $alias . '/a.php'; include $alias . '/b.php'; include $alias . '/b/c.php'; diff --git a/ext/reflection/tests/005.phpt b/ext/reflection/tests/005.phpt index f337e44ae..e257699b6 100644 --- a/ext/reflection/tests/005.phpt +++ b/ext/reflection/tests/005.phpt @@ -1,5 +1,8 @@ --TEST-- ReflectionMethod::getDocComment() uses wrong comment block +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php diff --git a/ext/reflection/tests/009.phpt b/ext/reflection/tests/009.phpt index e96b21eba..b54e89e5b 100644 --- a/ext/reflection/tests/009.phpt +++ b/ext/reflection/tests/009.phpt @@ -1,5 +1,8 @@ --TEST-- ReflectionFunction basic tests +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php diff --git a/ext/reflection/tests/025.phpt b/ext/reflection/tests/025.phpt index a5f604f56..92002007b 100644 --- a/ext/reflection/tests/025.phpt +++ b/ext/reflection/tests/025.phpt @@ -2,6 +2,9 @@ ReflectionFunction basic tests --SKIPIF-- <?php extension_loaded('reflection') or die('skip'); ?> +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php diff --git a/ext/reflection/tests/ReflectionClass_getDocComment_001.phpt b/ext/reflection/tests/ReflectionClass_getDocComment_001.phpt index 5feb560ae..efa7e9a10 100644 --- a/ext/reflection/tests/ReflectionClass_getDocComment_001.phpt +++ b/ext/reflection/tests/ReflectionClass_getDocComment_001.phpt @@ -3,6 +3,9 @@ ReflectionClass::getDocComment() --CREDITS-- Robin Fernandes <robinf@php.net> Steve Seear <stevseea@php.net> +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php /** diff --git a/ext/reflection/tests/ReflectionFunction_getDocComment.001.phpt b/ext/reflection/tests/ReflectionFunction_getDocComment.001.phpt index 38c278d8a..68d1d9d3a 100644 --- a/ext/reflection/tests/ReflectionFunction_getDocComment.001.phpt +++ b/ext/reflection/tests/ReflectionFunction_getDocComment.001.phpt @@ -3,6 +3,9 @@ ReflectionFunction::getDocComment() --CREDITS-- Robin Fernandes <robinf@php.net> Steve Seear <stevseea@php.net> +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php diff --git a/ext/reflection/tests/ReflectionMethod_getDocComment_basic.phpt b/ext/reflection/tests/ReflectionMethod_getDocComment_basic.phpt index c01f689cf..b04fb6c9f 100644 --- a/ext/reflection/tests/ReflectionMethod_getDocComment_basic.phpt +++ b/ext/reflection/tests/ReflectionMethod_getDocComment_basic.phpt @@ -1,5 +1,8 @@ --TEST-- ReflectionMethod::getDocComment() +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php /** diff --git a/ext/reflection/tests/ReflectionProperty_basic2.phpt b/ext/reflection/tests/ReflectionProperty_basic2.phpt index b7b21333d..bc42d3f82 100644 --- a/ext/reflection/tests/ReflectionProperty_basic2.phpt +++ b/ext/reflection/tests/ReflectionProperty_basic2.phpt @@ -1,5 +1,8 @@ --TEST-- Test usage of ReflectionProperty methods isDefault(), getModifiers(), getDeclaringClass() and getDocComment(). +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php diff --git a/ext/reflection/tests/ReflectionProperty_getDocComment_basic.phpt b/ext/reflection/tests/ReflectionProperty_getDocComment_basic.phpt index 2c4815a35..f94ee8ca4 100644 --- a/ext/reflection/tests/ReflectionProperty_getDocComment_basic.phpt +++ b/ext/reflection/tests/ReflectionProperty_getDocComment_basic.phpt @@ -1,5 +1,8 @@ --TEST-- Test ReflectionProperty::getDocComment() usage. +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php diff --git a/ext/reflection/tests/bug36308.phpt b/ext/reflection/tests/bug36308.phpt index 79aa5f8fa..f923ee31a 100644 --- a/ext/reflection/tests/bug36308.phpt +++ b/ext/reflection/tests/bug36308.phpt @@ -1,5 +1,8 @@ --TEST-- Reflection Bug #36308 (ReflectionProperty::getDocComment() does not reflect extended class commentary) +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php class Base { diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 692516840..e7c2f2984 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -59,7 +59,7 @@ static zval *sxe_get_value(zval *z TSRMLS_DC); static void php_sxe_iterator_dtor(zend_object_iterator *iter TSRMLS_DC); static int php_sxe_iterator_valid(zend_object_iterator *iter TSRMLS_DC); static void php_sxe_iterator_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC); -static int php_sxe_iterator_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC); +static void php_sxe_iterator_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC); static void php_sxe_iterator_move_forward(zend_object_iterator *iter TSRMLS_DC); static void php_sxe_iterator_rewind(zend_object_iterator *iter TSRMLS_DC); @@ -694,7 +694,7 @@ static void sxe_dimension_write(zval *object, zval *offset, zval *value TSRMLS_D } /* }}} */ -static zval** sxe_property_get_adr(zval *object, zval *member, const zend_literal *key TSRMLS_DC) /* {{{ */ +static zval** sxe_property_get_adr(zval *object, zval *member, int fetch_type, const zend_literal *key TSRMLS_DC) /* {{{ */ { php_sxe_object *sxe; xmlNodePtr node; @@ -2376,29 +2376,22 @@ static void php_sxe_iterator_current_data(zend_object_iterator *iter, zval ***da } /* }}} */ -static int php_sxe_iterator_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */ +static void php_sxe_iterator_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) /* {{{ */ { - zval *curobj; - xmlNodePtr curnode = NULL; - php_sxe_object *intern; - int namelen; - php_sxe_iterator *iterator = (php_sxe_iterator *)iter; - curobj = iterator->sxe->iter.data; + zval *curobj = iterator->sxe->iter.data; + php_sxe_object *intern = (php_sxe_object *)zend_object_store_get_object(curobj TSRMLS_CC); - intern = (php_sxe_object *)zend_object_store_get_object(curobj TSRMLS_CC); + xmlNodePtr curnode = NULL; if (intern != NULL && intern->node != NULL) { curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->node)->node; } - if (!curnode) { - return HASH_KEY_NON_EXISTANT; - } - - namelen = xmlStrlen(curnode->name); - *str_key = estrndup((char *)curnode->name, namelen); - *str_key_len = namelen + 1; - return HASH_KEY_IS_STRING; + if (curnode) { + ZVAL_STRINGL(key, (char *) curnode->name, xmlStrlen(curnode->name), 1); + } else { + ZVAL_NULL(key); + } } /* }}} */ diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 6f2f070f2..9789638c6 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -847,9 +847,9 @@ retry: } } else if (st & SNMP_USE_SUFFIX_AS_KEYS && st & SNMP_CMD_WALK) { snprint_objid(buf2, sizeof(buf2), vars->name, vars->name_length); - if (objid_query->vars[0].name_length <= vars->name_length && snmp_oid_compare(objid_query->vars[0].name, objid_query->vars[0].name_length, vars->name, objid_query->vars[0].name_length) == 0) { + if (rootlen <= vars->name_length && snmp_oid_compare(root, rootlen, vars->name, rootlen) == 0) { buf2[0] = '\0'; - count = objid_query->vars[0].name_length; + count = rootlen; while(count < vars->name_length){ sprintf(buf, "%lu.", vars->name[count]); strcat(buf2, buf); diff --git a/ext/snmp/tests/ipv6.phpt b/ext/snmp/tests/ipv6.phpt index 12879416c..f5239e117 100644 --- a/ext/snmp/tests/ipv6.phpt +++ b/ext/snmp/tests/ipv6.phpt @@ -20,6 +20,10 @@ snmp_set_quick_print(false); snmp_set_valueretrieval(SNMP_VALUE_PLAIN); var_dump(snmpget($hostname6_port, $community, '.1.3.6.1.2.1.1.1.0')); +var_dump(snmpget('[dead:beef::', $community, '.1.3.6.1.2.1.1.1.0')); ?> --EXPECTF-- %unicode|string%(%d) "%s" + +Warning: snmpget(): malformed IPv6 address, closing square bracket missing in %s on line %d +bool(false)
\ No newline at end of file diff --git a/ext/snmp/tests/snmp-object.phpt b/ext/snmp/tests/snmp-object.phpt index 06b6492bd..522d417af 100644 --- a/ext/snmp/tests/snmp-object.phpt +++ b/ext/snmp/tests/snmp-object.phpt @@ -83,6 +83,19 @@ var_dump(gettype($z)); var_dump(count($z)); var_dump(key($z)); var_dump(array_shift($z)); +var_dump(key($z)); +array_shift($z); +var_dump(key($z)); +array_shift($z); +var_dump(key($z)); +array_shift($z); +var_dump(key($z)); +array_shift($z); +var_dump(key($z)); +array_shift($z); +var_dump(key($z)); +array_shift($z); +var_dump(key($z)); var_dump($session->close()); echo "SNMPv3 (default security settings)\n"; @@ -194,6 +207,13 @@ string(5) "array" int(%d) string(3) "1.0" string(%d) "%s" +string(3) "2.0" +string(3) "3.0" +string(3) "4.0" +string(3) "5.0" +string(3) "6.0" +string(3) "7.0" +string(3) "8.0" bool(true) SNMPv3 (default security settings) string(%d) "%S" diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 897956d91..5cec3e558 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -2313,10 +2313,6 @@ static xmlNodePtr to_xml_array(encodeTypePtr type, zval *data, int style, xmlNod zend_object_iterator *iter; zend_class_entry *ce = Z_OBJCE_P(data); zval **val; - char *str_key; - uint str_key_len; - ulong int_key; - int key_type; ALLOC_ZVAL(array_copy); INIT_PZVAL(array_copy); @@ -2345,19 +2341,14 @@ static xmlNodePtr to_xml_array(encodeTypePtr type, zval *data, int style, xmlNod goto iterator_done; } if (iter->funcs->get_current_key) { - key_type = iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC); + zval key; + iter->funcs->get_current_key(iter, &key TSRMLS_CC); if (EG(exception)) { goto iterator_done; } - switch(key_type) { - case HASH_KEY_IS_STRING: - add_assoc_zval_ex(array_copy, str_key, str_key_len, *val); - efree(str_key); - break; - case HASH_KEY_IS_LONG: - add_index_zval(array_copy, int_key, *val); - break; - } + array_set_zval_key(Z_ARRVAL_P(array_copy), &key, *val); + zval_ptr_dtor(val); + zval_dtor(&key); } else { add_next_index_zval(array_copy, *val); } diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index e56a8f09e..edeade375 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -1018,20 +1018,20 @@ static void spl_array_it_get_current_data(zend_object_iterator *iter, zval ***da } /* }}} */ -static int spl_array_it_get_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */ +static void spl_array_it_get_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) /* {{{ */ { spl_array_it *iterator = (spl_array_it *)iter; spl_array_object *object = iterator->object; HashTable *aht = spl_array_get_hash_table(object, 0 TSRMLS_CC); if (object->ar_flags & SPL_ARRAY_OVERLOADED_KEY) { - return zend_user_it_get_current_key(iter, str_key, str_key_len, int_key TSRMLS_CC); + zend_user_it_get_current_key(iter, key TSRMLS_CC); } else { if (spl_array_object_verify_pos_ex(object, aht, "ArrayIterator::current(): " TSRMLS_CC) == FAILURE) { - return HASH_KEY_NON_EXISTANT; + ZVAL_NULL(key); + } else { + zend_hash_get_current_key_zval_ex(aht, key, &object->pos); } - - return zend_hash_get_current_key_ex(aht, str_key, str_key_len, int_key, 1, &object->pos); } } /* }}} */ @@ -1547,25 +1547,13 @@ SPL_METHOD(Array, key) void spl_array_iterator_key(zval *object, zval *return_value TSRMLS_DC) /* {{{ */ { spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC); - char *string_key; - uint string_length; - ulong num_key; HashTable *aht = spl_array_get_hash_table(intern, 0 TSRMLS_CC); if (spl_array_object_verify_pos(intern, aht TSRMLS_CC) == FAILURE) { return; } - switch (zend_hash_get_current_key_ex(aht, &string_key, &string_length, &num_key, 1, &intern->pos)) { - case HASH_KEY_IS_STRING: - RETVAL_STRINGL(string_key, string_length - 1, 0); - break; - case HASH_KEY_IS_LONG: - RETVAL_LONG(num_key); - break; - case HASH_KEY_NON_EXISTANT: - return; - } + zend_hash_get_current_key_zval_ex(aht, return_value, &intern->pos); } /* }}} */ diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index f43a3709e..cf653f689 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -1621,7 +1621,7 @@ SPL_METHOD(GlobIterator, count) static void spl_filesystem_dir_it_dtor(zend_object_iterator *iter TSRMLS_DC); static int spl_filesystem_dir_it_valid(zend_object_iterator *iter TSRMLS_DC); static void spl_filesystem_dir_it_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC); -static int spl_filesystem_dir_it_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC); +static void spl_filesystem_dir_it_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC); static void spl_filesystem_dir_it_move_forward(zend_object_iterator *iter TSRMLS_DC); static void spl_filesystem_dir_it_rewind(zend_object_iterator *iter TSRMLS_DC); @@ -1698,12 +1698,11 @@ static void spl_filesystem_dir_it_current_data(zend_object_iterator *iter, zval /* }}} */ /* {{{ spl_filesystem_dir_it_current_key */ -static int spl_filesystem_dir_it_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) +static void spl_filesystem_dir_it_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) { spl_filesystem_object *object = spl_filesystem_iterator_to_object((spl_filesystem_iterator *)iter); - - *int_key = object->u.dir.index; - return HASH_KEY_IS_LONG; + + ZVAL_LONG(key, object->u.dir.index); } /* }}} */ @@ -1777,19 +1776,16 @@ static void spl_filesystem_tree_it_current_data(zend_object_iterator *iter, zval /* }}} */ /* {{{ spl_filesystem_tree_it_current_key */ -static int spl_filesystem_tree_it_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) +static void spl_filesystem_tree_it_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) { spl_filesystem_object *object = spl_filesystem_iterator_to_object((spl_filesystem_iterator *)iter); - + if (SPL_FILE_DIR_KEY(object, SPL_FILE_DIR_KEY_AS_FILENAME)) { - *str_key_len = strlen(object->u.dir.entry.d_name) + 1; - *str_key = estrndup(object->u.dir.entry.d_name, *str_key_len - 1); + ZVAL_STRING(key, object->u.dir.entry.d_name, 1); } else { spl_filesystem_object_get_file_name(object TSRMLS_CC); - *str_key_len = object->file_name_len + 1; - *str_key = estrndup(object->file_name, object->file_name_len); + ZVAL_STRINGL(key, object->file_name, object->file_name_len, 1); } - return HASH_KEY_IS_STRING; } /* }}} */ diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index a8417fead..83610863e 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -794,7 +794,7 @@ SPL_METHOD(SplDoublyLinkedList, offsetGet) intern = (spl_dllist_object*)zend_object_store_get_object(getThis() TSRMLS_CC); index = spl_offset_convert_to_long(zindex TSRMLS_CC); - if (index < 0 || index >= intern->llist->count) { + if (index < 0 || index >= intern->llist->count) { zend_throw_exception(spl_ce_OutOfRangeException, "Offset invalid or out of range", 0 TSRMLS_CC); return; } @@ -880,10 +880,10 @@ SPL_METHOD(SplDoublyLinkedList, offsetUnset) } intern = (spl_dllist_object*)zend_object_store_get_object(getThis() TSRMLS_CC); - index = (int)spl_offset_convert_to_long(zindex TSRMLS_CC); - llist = intern->llist; + index = spl_offset_convert_to_long(zindex TSRMLS_CC); + llist = intern->llist; - if (index < 0 || index >= intern->llist->count) { + if (index < 0 || index >= intern->llist->count) { zend_throw_exception(spl_ce_OutOfRangeException, "Offset out of range", 0 TSRMLS_CC); return; } @@ -1023,12 +1023,11 @@ static void spl_dllist_it_get_current_data(zend_object_iterator *iter, zval ***d } /* }}} */ -static int spl_dllist_it_get_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */ +static void spl_dllist_it_get_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) /* {{{ */ { spl_dllist_it *iterator = (spl_dllist_it *)iter; - *int_key = (ulong) iterator->traverse_position; - return HASH_KEY_IS_LONG; + ZVAL_LONG(key, iterator->traverse_position); } /* }}} */ @@ -1139,7 +1138,7 @@ SPL_METHOD(SplDoublyLinkedList, serialize) spl_dllist_object *intern = (spl_dllist_object*)zend_object_store_get_object(getThis() TSRMLS_CC); smart_str buf = {0}; spl_ptr_llist_element *current = intern->llist->head, *next; - zval *flags; + zval *flags; php_serialize_data_t var_hash; if (zend_parse_parameters_none() == FAILURE) { @@ -1235,6 +1234,61 @@ error: } /* }}} */ +/* {{{ proto void SplDoublyLinkedList::add(mixed $index, mixed $newval) U + Inserts a new entry before the specified $index consisting of $newval. */ +SPL_METHOD(SplDoublyLinkedList, add) +{ + zval *zindex, *value; + spl_dllist_object *intern; + spl_ptr_llist_element *element; + long index; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &zindex, &value) == FAILURE) { + return; + } + SEPARATE_ARG_IF_REF(value); + + intern = (spl_dllist_object*)zend_object_store_get_object(getThis() TSRMLS_CC); + index = spl_offset_convert_to_long(zindex TSRMLS_CC); + + if (index < 0 || index > intern->llist->count) { + zend_throw_exception(spl_ce_OutOfRangeException, "Offset invalid or out of range", 0 TSRMLS_CC); + return; + } + + if (index == intern->llist->count) { + /* If index is the last entry+1 then we do a push because we're not inserting before any entry */ + spl_ptr_llist_push(intern->llist, value TSRMLS_CC); + } else { + /* Create the new element we want to insert */ + spl_ptr_llist_element *elem = emalloc(sizeof(spl_ptr_llist_element)); + + /* Get the element we want to insert before */ + element = spl_ptr_llist_offset(intern->llist, index, intern->flags & SPL_DLLIST_IT_LIFO); + + elem->data = value; + elem->rc = 1; + /* connect to the neighbours */ + elem->next = element; + elem->prev = element->prev; + + /* connect the neighbours to this new element */ + if (elem->prev == NULL) { + intern->llist->head = elem; + } else { + element->prev->next = elem; + } + element->prev = elem; + + intern->llist->count++; + + if (intern->llist->ctor) { + intern->llist->ctor(elem TSRMLS_CC); + } + } +} /* }}} */ + + /* iterator handler table */ zend_object_iterator_funcs spl_dllist_it_funcs = { spl_dllist_it_dtor, @@ -1322,6 +1376,9 @@ static const zend_function_entry spl_funcs_SplDoublyLinkedList[] = { SPL_ME(SplDoublyLinkedList, offsetGet, arginfo_dllist_offsetGet, ZEND_ACC_PUBLIC) SPL_ME(SplDoublyLinkedList, offsetSet, arginfo_dllist_offsetSet, ZEND_ACC_PUBLIC) SPL_ME(SplDoublyLinkedList, offsetUnset, arginfo_dllist_offsetGet, ZEND_ACC_PUBLIC) + + SPL_ME(SplDoublyLinkedList, add, arginfo_dllist_offsetSet, ZEND_ACC_PUBLIC) + /* Iterator */ SPL_ME(SplDoublyLinkedList, rewind, arginfo_dllist_void, ZEND_ACC_PUBLIC) SPL_ME(SplDoublyLinkedList, current, arginfo_dllist_void, ZEND_ACC_PUBLIC) diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index fec7e2c4a..86a5371ed 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -948,18 +948,16 @@ static void spl_fixedarray_it_get_current_data(zend_object_iterator *iter, zval } /* }}} */ -static int spl_fixedarray_it_get_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */ +static void spl_fixedarray_it_get_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) /* {{{ */ { spl_fixedarray_it *iterator = (spl_fixedarray_it *)iter; spl_fixedarray_object *intern = iterator->object; if (intern->flags & SPL_FIXEDARRAY_OVERLOADED_KEY) { - return zend_user_it_get_current_key(iter, str_key, str_key_len, int_key TSRMLS_CC); + zend_user_it_get_current_key(iter, key TSRMLS_CC); } else { - *int_key = (ulong) iterator->object->current; - return HASH_KEY_IS_LONG; + ZVAL_LONG(key, iterator->object->current); } - } /* }}} */ diff --git a/ext/spl/spl_heap.c b/ext/spl/spl_heap.c index d2de85b2a..cb1f68dcf 100644 --- a/ext/spl/spl_heap.c +++ b/ext/spl/spl_heap.c @@ -949,12 +949,11 @@ static void spl_pqueue_it_get_current_data(zend_object_iterator *iter, zval ***d } /* }}} */ -static int spl_heap_it_get_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */ +static void spl_heap_it_get_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) /* {{{ */ { spl_heap_it *iterator = (spl_heap_it *)iter; - *int_key = (ulong) iterator->object->heap->count - 1; - return HASH_KEY_IS_LONG; + ZVAL_LONG(key, iterator->object->heap->count - 1); } /* }}} */ diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index 87b3763cf..fcb4d20a6 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -190,16 +190,15 @@ static void spl_recursive_it_get_current_data(zend_object_iterator *iter, zval * sub_iter->funcs->get_current_data(sub_iter, data TSRMLS_CC); } -static int spl_recursive_it_get_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) +static void spl_recursive_it_get_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) { spl_recursive_it_object *object = (spl_recursive_it_object*)iter->data; zend_object_iterator *sub_iter = object->iterators[object->level].iterator; if (sub_iter->funcs->get_current_key) { - return sub_iter->funcs->get_current_key(sub_iter, str_key, str_key_len, int_key TSRMLS_CC); + sub_iter->funcs->get_current_key(sub_iter, key TSRMLS_CC); } else { - *int_key = iter->index; - return HASH_KEY_IS_LONG; + ZVAL_LONG(key, iter->index); } } @@ -617,20 +616,7 @@ SPL_METHOD(RecursiveIteratorIterator, key) } if (iterator->funcs->get_current_key) { - char *str_key; - uint str_key_len; - ulong int_key; - - switch (iterator->funcs->get_current_key(iterator, &str_key, &str_key_len, &int_key TSRMLS_CC)) { - case HASH_KEY_IS_LONG: - RETURN_LONG(int_key); - break; - case HASH_KEY_IS_STRING: - RETURN_STRINGL(str_key, str_key_len-1, 0); - break; - default: - RETURN_NULL(); - } + iterator->funcs->get_current_key(iterator, return_value TSRMLS_CC); } else { RETURN_NULL(); } @@ -1171,20 +1157,7 @@ SPL_METHOD(RecursiveTreeIterator, key) } if (iterator->funcs->get_current_key) { - char *str_key; - uint str_key_len; - ulong int_key; - - switch (iterator->funcs->get_current_key(iterator, &str_key, &str_key_len, &int_key TSRMLS_CC)) { - case HASH_KEY_IS_LONG: - ZVAL_LONG(&key, int_key); - break; - case HASH_KEY_IS_STRING: - ZVAL_STRINGL(&key, str_key, str_key_len-1, 0); - break; - default: - ZVAL_NULL(&key); - } + iterator->funcs->get_current_key(iterator, &key TSRMLS_CC); } else { ZVAL_NULL(&key); } @@ -1590,9 +1563,9 @@ static inline void spl_dual_it_free(spl_dual_it_object *intern TSRMLS_DC) zval_ptr_dtor(&intern->current.data); intern->current.data = NULL; } - if (intern->current.str_key) { - efree(intern->current.str_key); - intern->current.str_key = NULL; + if (intern->current.key) { + zval_ptr_dtor(&intern->current.key); + intern->current.key = NULL; } if (intern->dit_type == DIT_CachingIterator || intern->dit_type == DIT_RecursiveCachingIterator) { if (intern->u.caching.zstr) { @@ -1635,11 +1608,16 @@ static inline int spl_dual_it_fetch(spl_dual_it_object *intern, int check_more T intern->current.data = *data; Z_ADDREF_P(intern->current.data); } + + MAKE_STD_ZVAL(intern->current.key); if (intern->inner.iterator->funcs->get_current_key) { - intern->current.key_type = intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, &intern->current.str_key, &intern->current.str_key_len, &intern->current.int_key TSRMLS_CC); + intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, intern->current.key TSRMLS_CC); + if (EG(exception)) { + zval_ptr_dtor(&intern->current.key); + intern->current.key = NULL; + } } else { - intern->current.key_type = HASH_KEY_IS_LONG; - intern->current.int_key = intern->current.pos; + ZVAL_LONG(intern->current.key, intern->current.pos); } return EG(exception) ? FAILURE : SUCCESS; } @@ -1711,12 +1689,8 @@ SPL_METHOD(dual_it, key) SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); - if (intern->current.data) { - if (intern->current.key_type == HASH_KEY_IS_STRING) { - RETURN_STRINGL(intern->current.str_key, intern->current.str_key_len-1, 1); - } else { - RETURN_LONG(intern->current.int_key); - } + if (intern->current.key) { + RETURN_ZVAL(intern->current.key, 1, 0); } RETURN_NULL(); } /* }}} */ @@ -1927,27 +1901,18 @@ SPL_METHOD(CallbackFilterIterator, accept) zend_fcall_info *fci = &intern->u.cbfilter->fci; zend_fcall_info_cache *fcc = &intern->u.cbfilter->fcc; zval **params[3]; - zval zkey; - zval *zkey_p = &zkey; zval *result; if (zend_parse_parameters_none() == FAILURE) { return; } - if (intern->current.data == NULL) { + if (intern->current.data == NULL || intern->current.key == NULL) { RETURN_FALSE; } - - INIT_PZVAL(&zkey); - if (intern->current.key_type == HASH_KEY_IS_LONG) { - ZVAL_LONG(&zkey, intern->current.int_key); - } else { - ZVAL_STRINGL(&zkey, intern->current.str_key, intern->current.str_key_len-1, 0); - } params[0] = &intern->current.data; - params[1] = &zkey_p; + params[1] = &intern->current.key; params[2] = &intern->inner.zobject; fci->retval_ptr_ptr = &result; @@ -1971,9 +1936,9 @@ SPL_METHOD(CallbackFilterIterator, accept) SPL_METHOD(RegexIterator, accept) { spl_dual_it_object *intern; - char *subject, tmp[32], *result; + char *subject, *result; int subject_len, use_copy, count = 0, result_len; - zval subject_copy, zcount, *replacement, tmp_replacement; + zval *subject_ptr, subject_copy, zcount, *replacement, tmp_replacement; if (zend_parse_parameters_none() == FAILURE) { return; @@ -1986,24 +1951,18 @@ SPL_METHOD(RegexIterator, accept) } if (intern->u.regex.flags & REGIT_USE_KEY) { - if (intern->current.key_type == HASH_KEY_IS_LONG) { - subject_len = slprintf(tmp, sizeof(tmp), "%ld", intern->current.int_key); - subject = &tmp[0]; - use_copy = 0; - } else { - subject_len = intern->current.str_key_len - 1; - subject = estrndup(intern->current.str_key, subject_len); - use_copy = 1; - } + subject_ptr = intern->current.key; } else { - zend_make_printable_zval(intern->current.data, &subject_copy, &use_copy); - if (use_copy) { - subject = Z_STRVAL(subject_copy); - subject_len = Z_STRLEN(subject_copy); - } else { - subject = Z_STRVAL_P(intern->current.data); - subject_len = Z_STRLEN_P(intern->current.data); - } + subject_ptr = intern->current.data; + } + + zend_make_printable_zval(subject_ptr, &subject_copy, &use_copy); + if (use_copy) { + subject = Z_STRVAL(subject_copy); + subject_len = Z_STRLEN(subject_copy); + } else { + subject = Z_STRVAL_P(subject_ptr); + subject_len = Z_STRLEN_P(subject_ptr); } switch (intern->u.regex.mode) @@ -2051,12 +2010,9 @@ SPL_METHOD(RegexIterator, accept) result = php_pcre_replace_impl(intern->u.regex.pce, subject, subject_len, replacement, 0, &result_len, -1, &count TSRMLS_CC); if (intern->u.regex.flags & REGIT_USE_KEY) { - if (intern->current.key_type != HASH_KEY_IS_LONG) { - efree(intern->current.str_key); - } - intern->current.key_type = HASH_KEY_IS_STRING; - intern->current.str_key = result; - intern->current.str_key_len = result_len + 1; + zval_ptr_dtor(&intern->current.key); + MAKE_STD_ZVAL(intern->current.key); + ZVAL_STRINGL(intern->current.key, result, result_len, 0); } else { zval_ptr_dtor(&intern->current.data); MAKE_STD_ZVAL(intern->current.data); @@ -2590,14 +2546,14 @@ static inline void spl_caching_it_next(spl_dual_it_object *intern TSRMLS_DC) /* Full cache ? */ if (intern->u.caching.flags & CIT_FULL_CACHE) { zval *zcacheval; + zval *key = intern->current.key; MAKE_STD_ZVAL(zcacheval); ZVAL_ZVAL(zcacheval, intern->current.data, 1, 0); - if (intern->current.key_type == HASH_KEY_IS_LONG) { - add_index_zval(intern->u.caching.zcache, intern->current.int_key, zcacheval); - } else { - zend_symtable_update(HASH_OF(intern->u.caching.zcache), intern->current.str_key, intern->current.str_key_len, &zcacheval, sizeof(void*), NULL); - } + + array_set_zval_key(HASH_OF(intern->u.caching.zcache), key, zcacheval); + + zval_ptr_dtor(&zcacheval); } /* Recursion ? */ if (intern->dit_type == DIT_RecursiveCachingIterator) { @@ -2755,13 +2711,9 @@ SPL_METHOD(CachingIterator, __toString) return; } if (intern->u.caching.flags & CIT_TOSTRING_USE_KEY) { - if (intern->current.key_type == HASH_KEY_IS_STRING) { - RETURN_STRINGL(intern->current.str_key, intern->current.str_key_len-1, 1); - } else { - RETVAL_LONG(intern->current.int_key); - convert_to_string(return_value); - return; - } + MAKE_COPY_ZVAL(&intern->current.key, return_value); + convert_to_string(return_value); + return; } else if (intern->u.caching.flags & CIT_TOSTRING_USE_CURRENT) { MAKE_COPY_ZVAL(&intern->current.data, return_value); convert_to_string(return_value); @@ -3123,19 +3075,7 @@ SPL_METHOD(NoRewindIterator, key) SPL_FETCH_AND_CHECK_DUAL_IT(intern, getThis()); if (intern->inner.iterator->funcs->get_current_key) { - char *str_key; - uint str_key_len; - ulong int_key; - switch (intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, &str_key, &str_key_len, &int_key TSRMLS_CC)) { - case HASH_KEY_IS_LONG: - RETURN_LONG(int_key); - break; - case HASH_KEY_IS_STRING: - RETURN_STRINGL(str_key, str_key_len-1, 0); - break; - default: - RETURN_NULL(); - } + intern->inner.iterator->funcs->get_current_key(intern->inner.iterator, return_value TSRMLS_CC); } else { RETURN_NULL(); } @@ -3502,11 +3442,7 @@ done: static int spl_iterator_to_array_apply(zend_object_iterator *iter, void *puser TSRMLS_DC) /* {{{ */ { - zval **data, *return_value = (zval*)puser; - char *str_key; - uint str_key_len; - ulong int_key; - int key_type; + zval **data, *return_value = (zval*)puser; iter->funcs->get_current_data(iter, &data TSRMLS_CC); if (EG(exception)) { @@ -3516,20 +3452,13 @@ static int spl_iterator_to_array_apply(zend_object_iterator *iter, void *puser T return ZEND_HASH_APPLY_STOP; } if (iter->funcs->get_current_key) { - key_type = iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC); + zval key; + iter->funcs->get_current_key(iter, &key TSRMLS_CC); if (EG(exception)) { return ZEND_HASH_APPLY_STOP; } - Z_ADDREF_PP(data); - switch(key_type) { - case HASH_KEY_IS_STRING: - add_assoc_zval_ex(return_value, str_key, str_key_len, *data); - efree(str_key); - break; - case HASH_KEY_IS_LONG: - add_index_zval(return_value, int_key, *data); - break; - } + array_set_zval_key(Z_ARRVAL_P(return_value), &key, *data); + zval_dtor(&key); } else { Z_ADDREF_PP(data); add_next_index_zval(return_value, *data); diff --git a/ext/spl/spl_iterators.h b/ext/spl/spl_iterators.h index 39cc0d133..73d9d2e61 100644 --- a/ext/spl/spl_iterators.h +++ b/ext/spl/spl_iterators.h @@ -133,10 +133,7 @@ typedef struct _spl_dual_it_object { } inner; struct { zval *data; - char *str_key; - uint str_key_len; - ulong int_key; - int key_type; /* HASH_KEY_IS_STRING or HASH_KEY_IS_LONG */ + zval *key; int pos; } current; dual_it_type dit_type; diff --git a/ext/spl/tests/SplDoublyLinkedList_add_invalid_offset.phpt b/ext/spl/tests/SplDoublyLinkedList_add_invalid_offset.phpt new file mode 100644 index 000000000..b94b067f4 --- /dev/null +++ b/ext/spl/tests/SplDoublyLinkedList_add_invalid_offset.phpt @@ -0,0 +1,13 @@ +--TEST-- +Check that SplDoublyLinkedList::add throws an exception with an invalid offset argument +--FILE-- +<?php +try { + $dll = new SplDoublyLinkedList(); + var_dump($dll->add(12,'Offset 12 should not exist')); +} catch (OutOfRangeException $e) { + echo "Exception: ".$e->getMessage()."\n"; +} +?> +--EXPECTF-- +Exception: Offset invalid or out of range diff --git a/ext/spl/tests/SplDoublyLinkedList_add_missing_parameter1.phpt b/ext/spl/tests/SplDoublyLinkedList_add_missing_parameter1.phpt new file mode 100644 index 000000000..12cfe4000 --- /dev/null +++ b/ext/spl/tests/SplDoublyLinkedList_add_missing_parameter1.phpt @@ -0,0 +1,11 @@ +--TEST-- +Check that SplDoublyLinkedList::add generate a warning and returns a NULL with missing arguments +--FILE-- +<?php +$dll = new SplDoublyLinkedList(); +var_dump($dll->add()); +?> +--EXPECTF-- +Warning: SplDoublyLinkedList::add() expects exactly 2 parameters, 0 given in %s on line %d +NULL + diff --git a/ext/spl/tests/SplDoublyLinkedList_add_missing_parameter2.phpt b/ext/spl/tests/SplDoublyLinkedList_add_missing_parameter2.phpt new file mode 100644 index 000000000..c9c319316 --- /dev/null +++ b/ext/spl/tests/SplDoublyLinkedList_add_missing_parameter2.phpt @@ -0,0 +1,11 @@ +--TEST-- +Check that SplDoublyLinkedList::add generate a warning and returns a NULL with a missing value argument +--FILE-- +<?php +$dll = new SplDoublyLinkedList(); +var_dump($dll->add(2)); +?> +--EXPECTF-- +Warning: SplDoublyLinkedList::add() expects exactly 2 parameters, 1 given in %s on line %d +NULL + diff --git a/ext/spl/tests/SplDoublyLinkedList_add_null_offset.phpt b/ext/spl/tests/SplDoublyLinkedList_add_null_offset.phpt new file mode 100644 index 000000000..396f89b49 --- /dev/null +++ b/ext/spl/tests/SplDoublyLinkedList_add_null_offset.phpt @@ -0,0 +1,13 @@ +--TEST-- +Check that SplDoublyLinkedList::add throws an exception with an invalid offset argument +--FILE-- +<?php +try { + $dll = new SplDoublyLinkedList(); + var_dump($dll->add(NULL,2)); +} catch (OutOfRangeException $e) { + echo "Exception: ".$e->getMessage()."\n"; +} +?> +--EXPECTF-- +Exception: Offset invalid or out of range diff --git a/ext/spl/tests/dllist_013.phpt b/ext/spl/tests/dllist_013.phpt new file mode 100644 index 000000000..b60f06392 --- /dev/null +++ b/ext/spl/tests/dllist_013.phpt @@ -0,0 +1,45 @@ +--TEST-- +SPL: DoublyLinkedList: insert operations +--FILE-- +<?php +$dll = new SplDoublyLinkedList(); +// errors +try { + $dll->add(2,5); +} catch (OutOfRangeException $e) { + echo "Exception: ".$e->getMessage()."\n"; +} + +$dll->add(0,6); // 6 +$dll->add(0,3); // 3 6 +// Insert in the middle of the DLL +$dll->add(1,4); // 3 4 6 +$dll->add(2,5); // 3 4 5 6 +$dll->unshift(2); // 2 3 5 4 6 +// Insert at the beginning and end of the DLL +$dll->add(0,1); // 1 2 3 4 5 6 +$dll->add(6,7); // 1 2 3 4 5 6 7 + +echo count($dll)."\n"; + +echo $dll->pop()."\n"; +echo $dll->pop()."\n"; +echo $dll->pop()."\n"; +echo $dll->pop()."\n"; +echo $dll->pop()."\n"; +echo $dll->pop()."\n"; +echo $dll->pop()."\n"; +?> +===DONE=== +<?php exit(0); ?> +--EXPECTF-- +Exception: Offset invalid or out of range +7 +7 +6 +5 +4 +3 +2 +1 +===DONE=== diff --git a/ext/spl/tests/iterator_to_array_nonscalar_keys.phpt b/ext/spl/tests/iterator_to_array_nonscalar_keys.phpt new file mode 100644 index 000000000..4ca9485fa --- /dev/null +++ b/ext/spl/tests/iterator_to_array_nonscalar_keys.phpt @@ -0,0 +1,31 @@ +--TEST-- +Tests iterator_to_array() with non-scalar keys +--FILE-- +<?php + +function gen() { + yield "foo" => 0; + yield 1 => 1; + yield 2.5 => 2; + yield null => 3; + yield [] => 4; + yield new stdClass => 5; +} + +var_dump(iterator_to_array(gen())); + +?> +--EXPECTF-- +Warning: Illegal offset type in %s on line %d + +Warning: Illegal offset type in %s on line %d +array(4) { + ["foo"]=> + int(0) + [1]=> + int(1) + [2]=> + int(2) + [""]=> + int(3) +} diff --git a/ext/spl/tests/multiple_iterator_001.phpt b/ext/spl/tests/multiple_iterator_001.phpt index edd03f504..eb77f7937 100644 --- a/ext/spl/tests/multiple_iterator_001.phpt +++ b/ext/spl/tests/multiple_iterator_001.phpt @@ -23,8 +23,8 @@ echo "-- Default flags, MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_K var_dump($m->getFlags() === (MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_KEYS_NUMERIC)); -foreach($m as $value) { - var_dump($m->key(), $value); +foreach($m as $key => $value) { + var_dump($key, $value); } try { $m->current(); @@ -42,8 +42,8 @@ echo "-- Flags = MultipleIterator::MIT_NEED_ANY | MultipleIterator::MIT_KEYS_NUM $m->setFlags(MultipleIterator::MIT_NEED_ANY | MultipleIterator::MIT_KEYS_NUMERIC); var_dump($m->getFlags() === (MultipleIterator::MIT_NEED_ANY | MultipleIterator::MIT_KEYS_NUMERIC)); -foreach($m as $value) { - var_dump($m->key(), $value); +foreach($m as $key => $value) { + var_dump($key, $value); } echo "-- Default flags, added element --\n"; @@ -51,8 +51,8 @@ echo "-- Default flags, added element --\n"; $m->setFlags(MultipleIterator::MIT_NEED_ALL | MultipleIterator::MIT_KEYS_NUMERIC); $iter2[] = 3; -foreach($m as $value) { - var_dump($m->key(), $value); +foreach($m as $key => $value) { + var_dump($key, $value); } echo "-- Flags |= MultipleIterator::MIT_KEYS_ASSOC, with iterator associated with NULL --\n"; @@ -71,8 +71,8 @@ $m->attachIterator($iter1, "iter1"); $m->attachIterator($iter2, b"iter2"); $m->attachIterator($iter3, 3); -foreach($m as $value) { - var_dump($m->key(), $value); +foreach($m as $key => $value) { + var_dump($key, $value); } echo "-- Associate with invalid value --\n"; @@ -98,8 +98,8 @@ var_dump($m->containsIterator($iter2)); var_dump($m->detachIterator($iter2)); var_dump($m->countIterators()); var_dump($m->containsIterator($iter2)); -foreach($m as $value) { - var_dump($m->key(), $value); +foreach($m as $key => $value) { + var_dump($key, $value); } ?> diff --git a/ext/standard/array.c b/ext/standard/array.c index 40a27c053..226bfefc5 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -927,24 +927,12 @@ PHP_FUNCTION(current) PHP_FUNCTION(key) { HashTable *array; - char *string_key; - uint string_length; - ulong num_key; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H", &array) == FAILURE) { return; } - switch (zend_hash_get_current_key_ex(array, &string_key, &string_length, &num_key, 0, NULL)) { - case HASH_KEY_IS_STRING: - RETVAL_STRINGL(string_key, string_length - 1, 1); - break; - case HASH_KEY_IS_LONG: - RETVAL_LONG(num_key); - break; - case HASH_KEY_NON_EXISTANT: - return; - } + zend_hash_get_current_key_zval(array, return_value); } /* }}} */ @@ -1055,9 +1043,6 @@ static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive zval **args[3], /* Arguments to userland function */ *retval_ptr, /* Return value - unused */ *key=NULL; /* Entry key */ - char *string_key; - uint string_key_len; - ulong num_key; /* Set up known arguments */ args[1] = &key; @@ -1103,17 +1088,7 @@ static int php_array_walk(HashTable *target_hash, zval *userdata, int recursive } else { /* Allocate space for key */ MAKE_STD_ZVAL(key); - - /* Set up the key */ - switch (zend_hash_get_current_key_ex(target_hash, &string_key, &string_key_len, &num_key, 0, NULL)) { - case HASH_KEY_IS_LONG: - Z_TYPE_P(key) = IS_LONG; - Z_LVAL_P(key) = num_key; - break; - case HASH_KEY_IS_STRING: - ZVAL_STRINGL(key, string_key, string_key_len - 1, 1); - break; - } + zend_hash_get_current_key_zval(target_hash, key); /* Call the userland function */ if (zend_call_function(&BG(array_walk_fci), &BG(array_walk_fci_cache) TSRMLS_CC) == SUCCESS) { @@ -1205,9 +1180,6 @@ static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ res; /* comparison result */ HashPosition pos; /* hash iterator */ zend_bool strict = 0; /* strict comparison or not */ - ulong num_key; - uint str_key_len; - char *string_key; int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &value, &array, &strict) == FAILURE) { @@ -1225,15 +1197,8 @@ static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ if (behavior == 0) { RETURN_TRUE; } else { - /* Return current key */ - switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 0, &pos)) { - case HASH_KEY_IS_STRING: - RETURN_STRINGL(string_key, str_key_len - 1, 1); - break; - case HASH_KEY_IS_LONG: - RETURN_LONG(num_key); - break; - } + zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(array), return_value, &pos); + return; } } zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos); @@ -2447,9 +2412,6 @@ PHP_FUNCTION(array_keys) res, /* Result of comparison */ *new_val; /* New value */ int add_key; /* Flag to indicate whether a key should be added */ - char *string_key; /* String key */ - uint string_key_len; - ulong num_key; /* Numeric key */ zend_bool strict = 0; /* do strict comparison */ HashPosition pos; int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function; @@ -2480,19 +2442,8 @@ PHP_FUNCTION(array_keys) if (add_key) { MAKE_STD_ZVAL(new_val); - - switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(input), &string_key, &string_key_len, &num_key, 1, &pos)) { - case HASH_KEY_IS_STRING: - ZVAL_STRINGL(new_val, string_key, string_key_len - 1, 0); - zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL); - break; - - case HASH_KEY_IS_LONG: - Z_TYPE_P(new_val) = IS_LONG; - Z_LVAL_P(new_val) = num_key; - zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL); - break; - } + zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(input), new_val, &pos); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &new_val, sizeof(zval *), NULL); } zend_hash_move_forward_ex(Z_ARRVAL_P(input), &pos); @@ -2573,6 +2524,121 @@ PHP_FUNCTION(array_count_values) } /* }}} */ +/* {{{ proto array array_column(array input, mixed column_key[, mixed index_key]) + Return the values from a single column in the input array, identified by the + value_key and optionally indexed by the index_key */ +PHP_FUNCTION(array_column) +{ + zval *zarray, *zcolumn, *zkey = NULL, **data, **zcolval, **zkeyval; + HashTable *arr_hash; + HashPosition pointer; + ulong column_idx = 0, key_idx = 0; + char *column = NULL, *key = NULL, *keyval = NULL; + int column_len = 0, key_len = 0, keyval_idx = -1; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "az|z", &zarray, &zcolumn, &zkey) == FAILURE) { + return; + } + + switch (Z_TYPE_P(zcolumn)) { + case IS_NULL: + column_idx = 0; + break; + case IS_LONG: + column_idx = Z_LVAL_P(zcolumn); + break; + case IS_STRING: + column = Z_STRVAL_P(zcolumn); + column_len = Z_STRLEN_P(zcolumn); + break; + case IS_OBJECT: + convert_to_string(zcolumn); + column = Z_STRVAL_P(zcolumn); + column_len = Z_STRLEN_P(zcolumn); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "The column key should be either a string or an integer"); + RETURN_FALSE; + } + + if (zkey) { + switch (Z_TYPE_P(zkey)) { + case IS_NULL: + key_idx = 0; + break; + case IS_LONG: + key_idx = Z_LVAL_P(zkey); + break; + case IS_STRING: + key = Z_STRVAL_P(zkey); + key_len = Z_STRLEN_P(zkey); + break; + case IS_OBJECT: + convert_to_string(zkey); + key = Z_STRVAL_P(zkey); + key_len = Z_STRLEN_P(zkey); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "The index key should be either a string or an integer"); + RETURN_FALSE; + } + } + + arr_hash = Z_ARRVAL_P(zarray); + array_init(return_value); + + for (zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); + zend_hash_get_current_data_ex(arr_hash, (void**)&data, &pointer) == SUCCESS; + zend_hash_move_forward_ex(arr_hash, &pointer)) { + + if (Z_TYPE_PP(data) == IS_ARRAY) { + if (column && zend_hash_find(Z_ARRVAL_PP(data), column, column_len + 1, (void**)&zcolval) == FAILURE) { + continue; + } else if (!column && zend_hash_index_find(Z_ARRVAL_PP(data), column_idx, (void**)&zcolval) == FAILURE) { + continue; + } + + Z_ADDREF_PP(zcolval); + + keyval = NULL; + keyval_idx = -1; + + if (zkey) { + if (key && zend_hash_find(Z_ARRVAL_PP(data), key, key_len + 1, (void**)&zkeyval) == FAILURE) { + keyval_idx = -1; + } else if (!key && zend_hash_index_find(Z_ARRVAL_PP(data), key_idx, (void**)&zkeyval) == FAILURE) { + keyval_idx = -1; + } else { + switch (Z_TYPE_PP(zkeyval)) { + case IS_LONG: + keyval_idx = Z_LVAL_PP(zkeyval); + break; + case IS_STRING: + keyval = Z_STRVAL_PP(zkeyval); + break; + case IS_OBJECT: + convert_to_string(*zkeyval); + keyval = Z_STRVAL_PP(zkeyval); + break; + default: + keyval_idx = -1; + } + } + } + + if (keyval) { + add_assoc_zval(return_value, keyval, *zcolval); + } else if (keyval_idx != -1) { + add_index_zval(return_value, keyval_idx, *zcolval); + } else { + add_next_index_zval(return_value, *zcolval); + } + } + + } +} +/* }}} */ + /* {{{ proto array array_reverse(array input [, bool preserve keys]) Return input as a new array with the order of the entries reversed */ PHP_FUNCTION(array_reverse) @@ -2691,9 +2757,6 @@ PHP_FUNCTION(array_pad) PHP_FUNCTION(array_flip) { zval *array, **entry, *data; - char *string_key; - uint str_key_len; - ulong num_key; HashPosition pos; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) { @@ -2705,15 +2768,7 @@ PHP_FUNCTION(array_flip) zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos); while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&entry, &pos) == SUCCESS) { MAKE_STD_ZVAL(data); - switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 1, &pos)) { - case HASH_KEY_IS_STRING: - ZVAL_STRINGL(data, string_key, str_key_len - 1, 0); - break; - case HASH_KEY_IS_LONG: - Z_TYPE_P(data) = IS_LONG; - Z_LVAL_P(data) = num_key; - break; - } + zend_hash_get_current_key_zval_ex(Z_ARRVAL_P(array), data, &pos); if (Z_TYPE_PP(entry) == IS_LONG) { zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_PP(entry), &data, sizeof(data), NULL); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index a40fdd239..3a84d16d6 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -436,6 +436,12 @@ ZEND_BEGIN_ARG_INFO(arginfo_array_count_values, 0) ZEND_ARG_INFO(0, arg) /* ARRAY_INFO(0, arg, 0) */ ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_array_column, 0, 0, 2) + ZEND_ARG_INFO(0, arg) /* ARRAY_INFO(0, arg, 0) */ + ZEND_ARG_INFO(0, column_key) + ZEND_ARG_INFO(0, index_key) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_array_reverse, 0, 0, 1) ZEND_ARG_INFO(0, input) /* ARRAY_INFO(0, arg, 0) */ ZEND_ARG_INFO(0, preserve_keys) @@ -3323,6 +3329,7 @@ const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(array_keys, arginfo_array_keys) PHP_FE(array_values, arginfo_array_values) PHP_FE(array_count_values, arginfo_array_count_values) + PHP_FE(array_column, arginfo_array_column) PHP_FE(array_reverse, arginfo_array_reverse) PHP_FE(array_reduce, arginfo_array_reduce) PHP_FE(array_pad, arginfo_array_pad) diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index 942c33f9e..1cf277907 100644 --- a/ext/standard/php_array.h +++ b/ext/standard/php_array.h @@ -71,6 +71,7 @@ PHP_FUNCTION(array_replace_recursive); PHP_FUNCTION(array_keys); PHP_FUNCTION(array_values); PHP_FUNCTION(array_count_values); +PHP_FUNCTION(array_column); PHP_FUNCTION(array_reverse); PHP_FUNCTION(array_reduce); PHP_FUNCTION(array_pad); diff --git a/ext/standard/tests/array/array_column_basic.phpt b/ext/standard/tests/array/array_column_basic.phpt new file mode 100644 index 000000000..70ce2136b --- /dev/null +++ b/ext/standard/tests/array/array_column_basic.phpt @@ -0,0 +1,307 @@ +--TEST-- +Test array_column() function: basic functionality +--FILE-- +<?php +/* Prototype: + * array array_column(array $input, mixed $column_key[, mixed $index_key]); + * Description: + * Returns an array containing all the values from + * the specified "column" in a two-dimensional array. + */ + +echo "*** Testing array_column() : basic functionality ***\n"; +/* Array representing a possible record set returned from a database */ +$records = array( + array( + 'id' => 1, + 'first_name' => 'John', + 'last_name' => 'Doe' + ), + array( + 'id' => 2, + 'first_name' => 'Sally', + 'last_name' => 'Smith' + ), + array( + 'id' => 3, + 'first_name' => 'Jane', + 'last_name' => 'Jones' + ) +); + +echo "-- first_name column from recordset --\n"; +var_dump(array_column($records, 'first_name')); + +echo "-- id column from recordset --\n"; +var_dump(array_column($records, 'id')); + +echo "-- last_name column from recordset, keyed by value from id column --\n"; +var_dump(array_column($records, 'last_name', 'id')); + +echo "-- last_name column from recordset, keyed by value from first_name column --\n"; +var_dump(array_column($records, 'last_name', 'first_name')); + +echo "\n*** Testing multiple data types ***\n"; +$file = basename(__FILE__); +$fh = fopen($file, 'r', true); +$values = array( + array( + 'id' => 1, + 'value' => new stdClass + ), + array( + 'id' => 2, + 'value' => 34.2345 + ), + array( + 'id' => 3, + 'value' => true + ), + array( + 'id' => 4, + 'value' => false + ), + array( + 'id' => 5, + 'value' => null + ), + array( + 'id' => 6, + 'value' => 1234 + ), + array( + 'id' => 7, + 'value' => 'Foo' + ), + array( + 'id' => 8, + 'value' => $fh + ) +); +var_dump(array_column($values, 'value')); +var_dump(array_column($values, 'value', 'id')); + +echo "\n*** Testing numeric column keys ***\n"; +$numericCols = array( + array('aaa', '111'), + array('bbb', '222'), + array('ccc', '333') +); +var_dump(array_column($numericCols, 1)); +var_dump(array_column($numericCols, 1, 0)); + +echo "\n*** Testing failure to find specified column ***\n"; +var_dump(array_column($numericCols, 2)); +var_dump(array_column($numericCols, 'foo')); +var_dump(array_column($numericCols, 0, 'foo')); + +echo "\n*** Testing single dimensional array ***\n"; +$singleDimension = array('foo', 'bar', 'baz'); +var_dump(array_column($singleDimension, 1)); + +echo "\n*** Testing columns not present in all rows ***\n"; +$mismatchedColumns = array( + array('a' => 'foo', 'b' => 'bar', 'e' => 'bbb'), + array('a' => 'baz', 'c' => 'qux', 'd' => 'aaa'), + array('a' => 'eee', 'b' => 'fff', 'e' => 'ggg'), +); +var_dump(array_column($mismatchedColumns, 'c')); +var_dump(array_column($mismatchedColumns, 'c', 'a')); +var_dump(array_column($mismatchedColumns, 'a', 'd')); +var_dump(array_column($mismatchedColumns, 'a', 'e')); +var_dump(array_column($mismatchedColumns, 'b')); +var_dump(array_column($mismatchedColumns, 'b', 'a')); + +echo "\n*** Testing use of object converted to string ***\n"; +class Foo +{ + public function __toString() + { + return 'last_name'; + } +} +class Bar +{ + public function __toString() + { + return 'first_name'; + } +} +$f = new Foo(); +$b = new Bar(); +var_dump(array_column($records, $f)); +var_dump(array_column($records, $f, $b)); + +echo "Done\n"; +?> +--EXPECTF-- +*** Testing array_column() : basic functionality *** +-- first_name column from recordset -- +array(3) { + [0]=> + string(4) "John" + [1]=> + string(5) "Sally" + [2]=> + string(4) "Jane" +} +-- id column from recordset -- +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +-- last_name column from recordset, keyed by value from id column -- +array(3) { + [1]=> + string(3) "Doe" + [2]=> + string(5) "Smith" + [3]=> + string(5) "Jones" +} +-- last_name column from recordset, keyed by value from first_name column -- +array(3) { + ["John"]=> + string(3) "Doe" + ["Sally"]=> + string(5) "Smith" + ["Jane"]=> + string(5) "Jones" +} + +*** Testing multiple data types *** +array(8) { + [0]=> + object(stdClass)#1 (0) { + } + [1]=> + float(34.2345) + [2]=> + bool(true) + [3]=> + bool(false) + [4]=> + NULL + [5]=> + int(1234) + [6]=> + string(3) "Foo" + [7]=> + resource(5) of type (stream) +} +array(8) { + [1]=> + object(stdClass)#1 (0) { + } + [2]=> + float(34.2345) + [3]=> + bool(true) + [4]=> + bool(false) + [5]=> + NULL + [6]=> + int(1234) + [7]=> + string(3) "Foo" + [8]=> + resource(5) of type (stream) +} + +*** Testing numeric column keys *** +array(3) { + [0]=> + string(3) "111" + [1]=> + string(3) "222" + [2]=> + string(3) "333" +} +array(3) { + ["aaa"]=> + string(3) "111" + ["bbb"]=> + string(3) "222" + ["ccc"]=> + string(3) "333" +} + +*** Testing failure to find specified column *** +array(0) { +} +array(0) { +} +array(3) { + [0]=> + string(3) "aaa" + [1]=> + string(3) "bbb" + [2]=> + string(3) "ccc" +} + +*** Testing single dimensional array *** +array(0) { +} + +*** Testing columns not present in all rows *** +array(1) { + [0]=> + string(3) "qux" +} +array(1) { + ["baz"]=> + string(3) "qux" +} +array(3) { + [0]=> + string(3) "foo" + ["aaa"]=> + string(3) "baz" + [1]=> + string(3) "eee" +} +array(3) { + ["bbb"]=> + string(3) "foo" + [0]=> + string(3) "baz" + ["ggg"]=> + string(3) "eee" +} +array(2) { + [0]=> + string(3) "bar" + [1]=> + string(3) "fff" +} +array(2) { + ["foo"]=> + string(3) "bar" + ["eee"]=> + string(3) "fff" +} + +*** Testing use of object converted to string *** +array(3) { + [0]=> + string(3) "Doe" + [1]=> + string(5) "Smith" + [2]=> + string(5) "Jones" +} +array(3) { + ["John"]=> + string(3) "Doe" + ["Sally"]=> + string(5) "Smith" + ["Jane"]=> + string(5) "Jones" +} +Done diff --git a/ext/standard/tests/array/array_column_error.phpt b/ext/standard/tests/array/array_column_error.phpt new file mode 100644 index 000000000..1aec1acc6 --- /dev/null +++ b/ext/standard/tests/array/array_column_error.phpt @@ -0,0 +1,98 @@ +--TEST-- +Test array_column() function: error conditions +--FILE-- +<?php +/* Prototype: + * array array_column(array $input, mixed $column_key[, mixed $index_key]); + * Description: + * Returns an array containing all the values from + * the specified "column" in a two-dimensional array. + */ + +echo "*** Testing array_column() : error conditions ***\n"; + +echo "\n-- Testing array_column() function with Zero arguments --\n"; +var_dump(array_column()); + +echo "\n-- Testing array_column() function with One argument --\n"; +var_dump(array_column(array())); + +echo "\n-- Testing array_column() function with string as first parameter --\n"; +var_dump(array_column('foo', 0)); + +echo "\n-- Testing array_column() function with int as first parameter --\n"; +var_dump(array_column(1, 'foo')); + +echo "\n-- Testing array_column() column key parameter should be a string or an integer (testing bool) --\n"; +var_dump(array_column(array(), true)); + +echo "\n-- Testing array_column() column key parameter should be a string or integer (testing float) --\n"; +var_dump(array_column(array(), 2.3)); + +echo "\n-- Testing array_column() column key parameter should be a string or integer (testing array) --\n"; +var_dump(array_column(array(), array())); + +echo "\n-- Testing array_column() index key parameter should be a string or an integer (testing bool) --\n"; +var_dump(array_column(array(), 'foo', true)); + +echo "\n-- Testing array_column() index key parameter should be a string or integer (testing float) --\n"; +var_dump(array_column(array(), 'foo', 2.3)); + +echo "\n-- Testing array_column() index key parameter should be a string or integer (testing array) --\n"; +var_dump(array_column(array(), 'foo', array())); + +echo "Done\n"; +?> +--EXPECTF-- +*** Testing array_column() : error conditions *** + +-- Testing array_column() function with Zero arguments -- + +Warning: array_column() expects at least 2 parameters, 0 given in %s on line %d +NULL + +-- Testing array_column() function with One argument -- + +Warning: array_column() expects at least 2 parameters, 1 given in %s on line %d +NULL + +-- Testing array_column() function with string as first parameter -- + +Warning: array_column() expects parameter 1 to be array, string given in %s on line %d +NULL + +-- Testing array_column() function with int as first parameter -- + +Warning: array_column() expects parameter 1 to be array, integer given in %s on line %d +NULL + +-- Testing array_column() column key parameter should be a string or an integer (testing bool) -- + +Warning: array_column(): The column key should be either a string or an integer in %s on line %d +bool(false) + +-- Testing array_column() column key parameter should be a string or integer (testing float) -- + +Warning: array_column(): The column key should be either a string or an integer in %s on line %d +bool(false) + +-- Testing array_column() column key parameter should be a string or integer (testing array) -- + +Warning: array_column(): The column key should be either a string or an integer in %s on line %d +bool(false) + +-- Testing array_column() index key parameter should be a string or an integer (testing bool) -- + +Warning: array_column(): The index key should be either a string or an integer in %s on line %d +bool(false) + +-- Testing array_column() index key parameter should be a string or integer (testing float) -- + +Warning: array_column(): The index key should be either a string or an integer in %s on line %d +bool(false) + +-- Testing array_column() index key parameter should be a string or integer (testing array) -- + +Warning: array_column(): The index key should be either a string or an integer in %s on line %d +bool(false) +Done diff --git a/ext/standard/tests/array/uasort_variation9.phpt b/ext/standard/tests/array/uasort_variation9.phpt index 486042e5e..85578b020 100644 --- a/ext/standard/tests/array/uasort_variation9.phpt +++ b/ext/standard/tests/array/uasort_variation9.phpt @@ -14,7 +14,7 @@ echo "*** Testing uasort() : 'cmp_function' with reference arguments ***\n"; // comparison function /* Prototype : int cmp(mixed &$value1, mixed &$value2) - * Parameters : $value1 and $value2 - values recieved by reference + * Parameters : $value1 and $value2 - values received by reference * Return value : 0 - if both values are same * 1 - if value1 is greater than value2 * -1 - if value1 is less than value2 diff --git a/ext/standard/tests/bug64370_var1.phpt b/ext/standard/tests/bug64370_var1.phpt new file mode 100644 index 000000000..aca46a594 --- /dev/null +++ b/ext/standard/tests/bug64370_var1.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test bug #64370 microtime(true) less than $_SERVER['REQUEST_TIME_FLOAT'] +--FILE-- +<?php +echo "\$_SERVER['REQUEST_TIME']: {$_SERVER['REQUEST_TIME']}\n"; +echo "\$_SERVER['REQUEST_TIME_FLOAT']: {$_SERVER['REQUEST_TIME_FLOAT']}\n"; +echo "time(): " . time() . "\n"; +echo "microtime(true): " . microtime(true) . "\n"; +$d = (microtime(true)-$_SERVER['REQUEST_TIME_FLOAT'])*1000; +echo "created in $d ms\n"; +echo ((bool)($d >= 0)) . "\n"; +?> +===DONE=== +--EXPECTF-- +$_SERVER['REQUEST_TIME']: %d +$_SERVER['REQUEST_TIME_FLOAT']: %f +time(): %d +microtime(true): %f +created in %f ms +1 +===DONE=== diff --git a/ext/standard/tests/bug64370_var2.phpt b/ext/standard/tests/bug64370_var2.phpt new file mode 100644 index 000000000..d0d3590ea --- /dev/null +++ b/ext/standard/tests/bug64370_var2.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test bug #64370 sequential microtime(true) calls +--FILE-- +<?php + +$i = 0; +while(100000 > $i++) { + $m0 = microtime(true); + $m1 = microtime(true); + $d = $m1 - $m0; + + /*echo "$d\n";*/ + + if ($d < 0) { + die("failed in {$i}th iteration"); + } +} +echo "ok\n"; +?> +===DONE=== +--EXPECT-- +ok +===DONE=== diff --git a/ext/standard/tests/serialize/bug64354_1.phpt b/ext/standard/tests/serialize/bug64354_1.phpt new file mode 100644 index 000000000..e85749bcb --- /dev/null +++ b/ext/standard/tests/serialize/bug64354_1.phpt @@ -0,0 +1,25 @@ +--TEST-- +Bug #64354 (Unserialize array of objects whose class can't be autoloaded fail) +--FILE-- +<?php +spl_autoload_register( + function($class) { + throw new Exception("Failed"); + } +); + +try { + var_dump(unserialize('O:1:"A":0:{}')); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + var_dump(unserialize('a:2:{i:0;O:1:"A":0:{}i:1;O:1:"A":0:{}}')); +} catch (Exception $e) { + var_dump($e->getMessage()); +} +?> +--EXPECTF-- +string(6) "Failed" +string(6) "Failed" diff --git a/ext/standard/tests/serialize/bug64354_2.phpt b/ext/standard/tests/serialize/bug64354_2.phpt new file mode 100644 index 000000000..41a455b54 --- /dev/null +++ b/ext/standard/tests/serialize/bug64354_2.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #64354 (Unserialize array of objects whose class can't be autoloaded fail) +--FILE-- +<?php +class A { + public function __wakeup() { + throw new Exception("Failed"); + } +} + +spl_autoload_register( + function($class) { + throw new Exception("Failed"); + } +); + +try { + var_dump(unserialize('a:2:{i:0;O:1:"A":0:{}i:1;O:1:"B":0:{}}')); +} catch (Exception $e) { + var_dump($e->getMessage()); +} +?> +--EXPECTF-- +string(6) "Failed" diff --git a/ext/standard/tests/serialize/bug64354_3.phpt b/ext/standard/tests/serialize/bug64354_3.phpt new file mode 100644 index 000000000..3ce61152d --- /dev/null +++ b/ext/standard/tests/serialize/bug64354_3.phpt @@ -0,0 +1,29 @@ +--TEST-- +Bug #64354 (Unserialize array of objects whose class can't be autoloaded fail) +--FILE-- +<?php +class A { + public function __sleep() { + throw new Exception("Failed"); + } +} + +class B implements Serializable { + public function serialize() { + return NULL; + } + + public function unserialize($data) { + } +} + +$data = array(new A, new B); + +try { + serialize($data); +} catch (Exception $e) { + var_dump($e->getMessage()); +} +?> +--EXPECTF-- +string(6) "Failed" diff --git a/ext/standard/var.c b/ext/standard/var.c index b13edf661..f76a14cfa 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -714,6 +714,10 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var ulong *var_already; HashTable *myht; + if (EG(exception)) { + return; + } + if (var_hash && php_add_var_hash(var_hash, struc, (void *) &var_already TSRMLS_CC) == FAILURE) { if (Z_ISREF_P(struc)) { smart_str_appendl(buf, "R:", 2); @@ -800,8 +804,15 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var BG(serialize_lock)++; res = call_user_function_ex(CG(function_table), &struc, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC); BG(serialize_lock)--; + + if (EG(exception)) { + if (retval_ptr) { + zval_ptr_dtor(&retval_ptr); + } + return; + } - if (res == SUCCESS && !EG(exception)) { + if (res == SUCCESS) { if (retval_ptr) { if (HASH_OF(retval_ptr)) { php_var_serialize_class(buf, struc, retval_ptr, var_hash TSRMLS_CC); @@ -921,6 +932,11 @@ PHP_FUNCTION(serialize) php_var_serialize(&buf, struc, &var_hash TSRMLS_CC); PHP_VAR_SERIALIZE_DESTROY(var_hash); + if (EG(exception)) { + smart_str_free(&buf); + RETURN_FALSE; + } + if (buf.c) { RETURN_STRINGL(buf.c, buf.len, 0); } else { @@ -951,7 +967,9 @@ PHP_FUNCTION(unserialize) if (!php_var_unserialize(&return_value, &p, p + buf_len, &var_hash TSRMLS_CC)) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); zval_dtor(return_value); - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + if (!EG(exception)) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + } RETURN_FALSE; } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index 03cf8dc60..2e8b6f62b 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Mon Jan 21 11:41:53 2013 */ +/* Generated by re2c 0.13.5 */ /* +----------------------------------------------------------------------+ | PHP Version 5 | @@ -394,8 +394,13 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) BG(serialize_lock)--; } - if (retval_ptr) + if (retval_ptr) { zval_ptr_dtor(&retval_ptr); + } + + if (EG(exception)) { + return 0; + } return finish_nested_data(UNSERIALIZE_PASSTHRU); @@ -615,10 +620,19 @@ yy20: BG(serialize_lock) = 1; if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { BG(serialize_lock) = 0; + if (EG(exception)) { + efree(class_name); + return 0; + } ce = *pce; break; } BG(serialize_lock) = 0; + + if (EG(exception)) { + efree(class_name); + return 0; + } /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { @@ -636,6 +650,12 @@ yy20: BG(serialize_lock) = 1; if (call_user_function_ex(CG(function_table), NULL, user_func, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) { BG(serialize_lock) = 0; + if (EG(exception)) { + efree(class_name); + zval_ptr_dtor(&user_func); + zval_ptr_dtor(&arg_func_name); + return 0; + } php_error_docref(NULL TSRMLS_CC, E_WARNING, "defined (%s) but not found", user_func->value.str.val); incomplete_class = 1; ce = PHP_IC_ENTRY; @@ -647,6 +667,12 @@ yy20: if (retval_ptr) { zval_ptr_dtor(&retval_ptr); } + if (EG(exception)) { + efree(class_name); + zval_ptr_dtor(&user_func); + zval_ptr_dtor(&arg_func_name); + return 0; + } /* The callback function may have defined the class */ if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 204995783..4d99cbfd7 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -400,8 +400,13 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) BG(serialize_lock)--; } - if (retval_ptr) + if (retval_ptr) { zval_ptr_dtor(&retval_ptr); + } + + if (EG(exception)) { + return 0; + } return finish_nested_data(UNSERIALIZE_PASSTHRU); @@ -681,10 +686,19 @@ object ":" uiv ":" ["] { BG(serialize_lock) = 1; if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { BG(serialize_lock) = 0; + if (EG(exception)) { + efree(class_name); + return 0; + } ce = *pce; break; } BG(serialize_lock) = 0; + + if (EG(exception)) { + efree(class_name); + return 0; + } /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { @@ -702,6 +716,12 @@ object ":" uiv ":" ["] { BG(serialize_lock) = 1; if (call_user_function_ex(CG(function_table), NULL, user_func, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) { BG(serialize_lock) = 0; + if (EG(exception)) { + efree(class_name); + zval_ptr_dtor(&user_func); + zval_ptr_dtor(&arg_func_name); + return 0; + } php_error_docref(NULL TSRMLS_CC, E_WARNING, "defined (%s) but not found", user_func->value.str.val); incomplete_class = 1; ce = PHP_IC_ENTRY; @@ -713,6 +733,12 @@ object ":" uiv ":" ["] { if (retval_ptr) { zval_ptr_dtor(&retval_ptr); } + if (EG(exception)) { + efree(class_name); + zval_ptr_dtor(&user_func); + zval_ptr_dtor(&arg_func_name); + return 0; + } /* The callback function may have defined the class */ if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { diff --git a/ext/zip/lib/zip_close.c b/ext/zip/lib/zip_close.c index 362f92d74..e66c56697 100644 --- a/ext/zip/lib/zip_close.c +++ b/ext/zip/lib/zip_close.c @@ -88,6 +88,9 @@ zip_close(struct zip *za) if (za == NULL) return -1; + if (za->zp == NULL) + return -1; + if (!_zip_changed(za, &survivors)) { _zip_free(za); return 0; @@ -164,9 +167,10 @@ zip_close(struct zip *za) for (j=0; j<survivors; j++) { i = filelist[j].idx; + _zip_dirent_init(&de); + /* create new local directory entry */ if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) { - _zip_dirent_init(&de); if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) _zip_dirent_torrent_normalize(&de); diff --git a/ext/zip/lib/zip_dirent.c b/ext/zip/lib/zip_dirent.c index b5b9d273b..b9dac5c98 100644 --- a/ext/zip/lib/zip_dirent.c +++ b/ext/zip/lib/zip_dirent.c @@ -157,11 +157,17 @@ _zip_cdir_write(struct zip_cdir *cd, FILE *fp, struct zip_error *error) void _zip_dirent_finalize(struct zip_dirent *zde) { - free(zde->filename); + if (zde->filename_len > 0) { + free(zde->filename); + } zde->filename = NULL; - free(zde->extrafield); + if (zde->extrafield_len > 0) { + free(zde->extrafield); + } zde->extrafield = NULL; - free(zde->comment); + if (zde->comment_len > 0) { + free(zde->comment); + } zde->comment = NULL; } |