diff options
182 files changed, 17292 insertions, 1817 deletions
@@ -1,8 +1,42 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +21 Mar 2013, PHP 5.5.0 Beta 1 + +- Core: + . Added Zend Opcache extension and enable building it by default. + More details here: https://wiki.php.net/rfc/optimizerplus. (Dmitry) + . Added array_column function which returns a column in a multidimensional + array. https://wiki.php.net/rfc/array_column. (Ben Ramsey) + . Fixed bug #64354 (Unserialize array of objects whose class can't + be autoloaded fail). (Laruence) + . Added support for changing the process's title in CLI/CLI-Server SAPIs. + The implementation is more robust that the proctitle PECL module. More + details here: https://wiki.php.net/rfc/cli_process_title. (Keyur) + . Fixed bug #64370 (microtime(true) less than $_SERVER['REQUEST_TIME_FLOAT']). + (Anatol) + . Added support for non-scalar Iterator keys in foreach + (https://wiki.php.net/rfc/foreach-non-scalar-keys). (Nikita Popov) + +- mysqlnd + . Fixed bug #63530 (mysqlnd_stmt::bind_one_parameter crashes, uses wrong alloc + for stmt->param_bind). (Andrey) + +- DateTime + . Fixed bug #53437 (Crash when using unserialized DatePeriod instance). + (Gustavo, Derick, Anatol) + . Fixed bug #62852 (Unserialize Invalid Date causes crash). (Anatol) + +- SPL: + . Implement FR #48358 (Add SplDoublyLinkedList::add() to insert an element + at a given offset). (Mark Baker, David Soria Parra) + +- Zip: + . Bug #64452 (Zip crash intermittently). (Anatol) + 07 Mar 2013, PHP 5.5.0 Alpha 6 - Core: + . Fixed bug #61025 (__invoke() visibility not honored). (Laruence) . Fixed bug #49348 (Uninitialized ++$foo->bar; does not cause a notice). (Stas) @@ -16,6 +50,10 @@ PHP NEWS - DateTime: . Fixed bug #64359 (strftime crash with VS2012). (Anatol) +- SNMP: + . Fixed bug #61981 (OO API, walk: $suffix_as_key is not working correctly). + (Boris Lytochkin) + 21 Feb 2013, PHP 5.5.0 Alpha 5 - Core: diff --git a/NEWS-5.5 b/NEWS-5.5 deleted file mode 100644 index a5b11c0d3..000000000 --- a/NEWS-5.5 +++ /dev/null @@ -1,344 +0,0 @@ -PHP NEWS -||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? 201?, PHP 5.5.0 Beta 1 - -- Core: - . Fixed bug #49348 (Uninitialized ++$foo->bar; does not cause a notice). - (Stas) - -- Sockets: - . Fixed bug #64287 (sendmsg/recvmsg shutdown handler causes segfault). - (Gustavo) - -- PCRE: - . Merged PCRE 8.32. (Anatol) - -21 Feb 2013, PHP 5.5.0 Alpha 5 - -- Core: - . Implemented FR #64175 (Added HTTP codes as of RFC 6585). (Jonh Wendell) - . Fixed bug #64135 (Exceptions from set_error_handler are not always - propagated). (Laruence) - . Fixed bug #63830 (Segfault on undefined function call in nested generator). - (Nikita Popov) - . Fixed bug #60833 (self, parent, static behave inconsistently - case-sensitive). (Stas, mario at include-once dot org) - . Implemented FR #60524 (specify temp dir by php.ini). (ALeX Kazik). - . Fixed bug #64142 (dval to lval different behavior on ppc64). (Remi) - . Added ARMv7/v8 versions of various Zend arithmetic functions that are - implemented using inline assembler (Ard Biesheuvel) - . Fix undefined behavior when converting double variables to integers. - The double is now always rounded towards zero, the remainder of its division - by 2^32 or 2^64 (depending on sizeof(long)) is calculated and it's made - signed assuming a two's complement representation. (Gustavo) - -- CLI server: - . Fixed bug #64128 (buit-in web server is broken on ppc64). (Remi) - -- cURL: - . Implemented FR #46439 - added CURLFile for safer file uploads. - (Stas) - -- Intl: - . Cherry-picked UConverter wrapper, which had accidentaly been committed only - to master. - -- mysqli - . Added mysqli_begin_transaction()/mysqli::begin_transaction(). Implemented - all options, per MySQL 5.6, which can be used with START TRANSACTION, COMMIT - and ROLLBACK through options to mysqli_commit()/mysqli_rollback() and their - respective OO counterparts. They work in libmysql and mysqlnd mode. (Andrey) - . Added mysqli_savepoint(), mysqli_release_savepoint(). (Andrey) - -- mysqlnd - . Add new begin_transaction() call to the connection object. Implemented all - options, per MySQL 5.6, which can be used with START TRANSACTION, COMMIT - and ROLLBACK. (Andrey) - . Added mysqlnd_savepoint(), mysqlnd_release_savepoint(). (Andrey) - -- Sockets: - . Added recvmsg() and sendmsg() wrappers. (Gustavo) - See https://wiki.php.net/rfc/sendrecvmsg - -- Filter: - . Implemented FR #49180 - added MAC address validation. (Martin) - -- Phar: - . Fixed timestamp update on Phar contents modification. (Dmitry) - -- SPL: - . Fixed bug #64264 (SPLFixedArray toArray problem). (Laruence) - . Fixed bug #64228 (RecursiveDirectoryIterator always assumes SKIP_DOTS). - (patch by kriss@krizalys.com, Laruence) - . Fixed bug #64106 (Segfault on SplFixedArray[][x] = y when extended). - (Nikita Popov) - . Fixed bug #52861 (unset fails with ArrayObject and deep arrays). - (Mike Willbanks) - -- SNMP: - . Fixed bug #64124 (IPv6 malformed). (Boris Lytochkin) - -24 Jan 2013, PHP 5.5.0 Alpha 4 - -- Core: - . Fixed bug #63980 (object members get trimmed by zero bytes). (Laruence) - . Implemented RFC for Class Name Resolution As Scalar Via "class" Keyword. - (Ralph Schindler, Nikita Popov, Lars) - -- DateTime - . Added DateTimeImmutable - a variant of DateTime that only returns the - modified state instead of changing itself. (Derick) - -- FPM: - . Fixed bug #63999 (php with fpm fails to build on Solaris 10 or 11). (Adam) - -- pgsql: - . Bug #46408: Locale number format settings can cause pg_query_params to - break with numerics. (asmecher, Lars) - -- dba: - . Bug #62489: dba_insert not working as expected. - (marc-bennewitz at arcor dot de, Lars) - -- Reflection: - . Fixed bug #64007 (There is an ability to create instance of Generator by - hand). (Laruence) - -10 Jan 2013, PHP 5.5.0 Alpha 3 - -- General improvements: - . Fixed bug #63874 (Segfault if php_strip_whitespace has heredoc). (Pierrick) - . Fixed bug #63822 (Crash when using closures with ArrayAccess). - (Nikita Popov) - . Add Generator::throw() method. (Nikita Popov) - . Bug #23955: allow specifying Max-Age attribute in setcookie() (narfbg, Lars) - . Bug #52126: timestamp for mail.log (Martin Jansen, Lars) - -- mysqlnd - . Fixed return value of mysqli_stmt_affected_rows() in the time after - prepare() and before execute(). (Andrey) - -- cURL: - . Added new functions curl_escape, curl_multi_setopt, curl_multi_strerror - curl_pause, curl_reset, curl_share_close, curl_share_init, - curl_share_setopt curl_strerror and curl_unescape. (Pierrick) - . Addes new curl options CURLOPT_TELNETOPTIONS, CURLOPT_GSSAPI_DELEGATION, - CURLOPT_ACCEPTTIMEOUT_MS, CURLOPT_SSL_OPTIONS, CURLOPT_TCP_KEEPALIVE, - CURLOPT_TCP_KEEPIDLE and CURLOPT_TCP_KEEPINTVL. (Pierrick) - -18 Dec 2012, PHP 5.5.0 Alpha 2 - -- General improvements: - . Added systemtap support by enabling systemtap compatible dtrace probes on - linux. (David Soria Parra) - . Added support for using empty() on the result of function calls and - other expressions (https://wiki.php.net/rfc/empty_isset_exprs). - (Nikita Popov) - . Optimized access to temporary and compiled VM variables. 8% less memory - reads. (Dmitry) - . The VM stacks for passing function arguments and syntaticaly nested calls - were merged into a single stack. The stack size needed for op_array - execution is calculated at compile time and preallocated at once. As result - all the stack push operatins don't require checks for stack overflow - any more. (Dmitry) - -- MySQL - . This extension is now deprecated, and deprecation warnings will be generated - when connections are established to databases via mysql_connect(), - mysql_pconnect(), or through implicit connection: use MySQLi or PDO_MySQL - instead (https://wiki.php.net/rfc/mysql_deprecation). (Adam) - -- Fileinfo: - . Fixed bug #63590 (Different results in TS and NTS under Windows). - (Anatoliy) - -- Apache2 Handler SAPI: - . Enabled Apache 2.4 configure option for Windows (Pierre, Anatoliy) - -13 Nov 2012, PHP 5.5.0 Alpha 1 - -- General improvements: - . Added generators and coroutines (https://wiki.php.net/rfc/generators). - (Nikita Popov) - . Added "finally" keyword (https://wiki.php.net/rfc/finally). (Laruence) - . Add simplified password hashing API - (https://wiki.php.net/rfc/password_hash). (Anthony Ferrara) - . Added support for list in foreach (https://wiki.php.net/rfc/foreachlist). - (Laruence) - . Added support for using empty() on the result of function calls and - other expressions (https://wiki.php.net/rfc/empty_isset_exprs). - (Nikita Popov) - . Added support for constant array/string dereferencing. (Laruence) - . Improve set_exception_handler while doing reset.(Laruence) - . Remove php_logo_guid(), php_egg_logo_guid(), php_real_logo_guid(), - zend_logo_guid(). (Adnrew Faulds) - . Drop Windows XP and 2003 support. (Pierre) - -- Calendar: - . Fixed bug #54254 (cal_from_jd returns month = 6 when there is only one Adar) - (Stas, Eitan Mosenkis) - -- Core: - . Added boolval(). (Jille Timmermans) - . Added "Z" option to pack/unpack. (Gustavo) - . Implemented FR #60738 (Allow 'set_error_handler' to handle NULL). - (Laruence, Nikita Popov) - . Added optional second argument for assert() to specify custom message. Patch - by Lonny Kapelushnik (lonny@lonnylot.com). (Lars) - . Fixed bug #18556 (Engine uses locale rules to handle class names). (Stas) - . Fixed bug #61681 (Malformed grammar). (Nikita Popov, Etienne, Laruence) - . Fixed bug #61038 (unpack("a5", "str\0\0") does not work as expected). - (srgoogleguy, Gustavo) - . Return previous handler when passing NULL to set_error_handler and - set_exception_handler. (Nikita Popov) - -- cURL: - . Added support for CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPT_APPEND, - CURLOPT_DIRLISTONLY, CURLOPT_NEW_DIRECTORY_PERMS, CURLOPT_NEW_FILE_PERMS, - CURLOPT_NETRC_FILE, CURLOPT_PREQUOTE, CURLOPT_KRBLEVEL, CURLOPT_MAXFILESIZE, - CURLOPT_FTP_ACCOUNT, CURLOPT_COOKIELIST, CURLOPT_IGNORE_CONTENT_LENGTH, - CURLOPT_CONNECT_ONLY, CURLOPT_LOCALPORT, CURLOPT_LOCALPORTRANGE, - CURLOPT_FTP_ALTERNATIVE_TO_USER, CURLOPT_SSL_SESSIONID_CACHE, - CURLOPT_FTP_SSL_CCC, CURLOPT_HTTP_CONTENT_DECODING, - CURLOPT_HTTP_TRANSFER_DECODING, CURLOPT_PROXY_TRANSFER_MODE, - CURLOPT_ADDRESS_SCOPE, CURLOPT_CRLFILE, CURLOPT_ISSUERCERT, - CURLOPT_USERNAME, CURLOPT_PASSWORD, CURLOPT_PROXYUSERNAME, - CURLOPT_PROXYPASSWORD, CURLOPT_NOPROXY, CURLOPT_SOCKS5_GSSAPI_NEC, - CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOPT_TFTP_BLKSIZE, - CURLOPT_SSH_KNOWNHOSTS, CURLOPT_FTP_USE_PRET, CURLOPT_MAIL_FROM, - CURLOPT_MAIL_RCPT, CURLOPT_RTSP_CLIENT_CSEQ, CURLOPT_RTSP_SERVER_CSEQ, - CURLOPT_RTSP_SESSION_ID, CURLOPT_RTSP_STREAM_URI, CURLOPT_RTSP_TRANSPORT, - CURLOPT_RTSP_REQUEST, CURLOPT_RESOLVE, CURLOPT_ACCEPT_ENCODING, - CURLOPT_TRANSFER_ENCODING, CURLOPT_DNS_SERVERS and CURLOPT_USE_SSL. - (Pierrick) - . Fixed bug #55635 (CURLOPT_BINARYTRANSFER no longer used. The constant - still exists for backward compatibility but is doing nothing). (Pierrick) - . Fixed bug #54995 (Missing CURLINFO_RESPONSE_CODE support). (Pierrick) - -- Datetime - . Fixed bug #61642 (modify("+5 weekdays") returns Sunday). - (Dmitri Iouchtchenko) - -- Hash - . Added support for PBKDF2 via hash_pbkdf2(). (Anthony Ferrara) - -- Intl - . The intl extension now requires ICU 4.0+. - . Added intl.use_exceptions INI directive, which controls what happens when - global errors are set together with intl.error_level. (Gustavo) - . MessageFormatter::format() and related functions now accepted named - arguments and mixed numeric/named arguments in ICU 4.8+. (Gustavo) - . MessageFormatter::format() and related functions now don't error out when - an insufficient argument count is provided. Instead, the placeholders will - remain unsubstituted. (Gustavo) - . MessageFormatter::parse() and MessageFormat::format() (and their static - equivalents) don't throw away better than second precision in the arguments. - (Gustavo) - . IntlDateFormatter::__construct and datefmt_create() now accept for the - $timezone argument time zone identifiers, IntlTimeZone objects, DateTimeZone - objects and NULL. (Gustavo) - . IntlDateFormatter::__construct and datefmt_create() no longer accept invalid - timezone identifiers or empty strings. (Gustavo) - . The default time zone used in IntlDateFormatter::__construct and - datefmt_create() (when the corresponding argument is not passed or NULL is - passed) is now the one given by date_default_timezone_get(), not the - default ICU time zone. (Gustavo) - . The time zone passed to the IntlDateFormatter is ignored if it is NULL and - if the calendar passed is an IntlCalendar object -- in this case, the - IntlCalendar's time zone will be used instead. Otherwise, the time zone - specified in the $timezone argument is used instead. This does not affect - old code, as IntlCalendar was introduced in this version. (Gustavo) - . IntlDateFormatter::__construct and datefmt_create() now accept for the - $calendar argument also IntlCalendar objects. (Gustavo) - . IntlDateFormatter::getCalendar() and datefmt_get_calendar() return false - if the IntlDateFormatter was set up with an IntlCalendar instead of the - constants IntlDateFormatter::GREGORIAN/TRADITIONAL. IntlCalendar did not - exist before this version. (Gustavo) - . IntlDateFormatter::setCalendar() and datefmt_set_calendar() now also accept - an IntlCalendar object, in which case its time zone is taken. Passing a - constant is still allowed, and still keeps the time zone. (Gustavo) - . IntlDateFormatter::setTimeZoneID() and datefmt_set_timezone_id() are - deprecated. Use IntlDateFormatter::setTimeZone() or datefmt_set_timezone() - instead. (Gustavo) - . IntlDateFormatter::format() and datefmt_format() now also accept an - IntlCalendar object for formatting. (Gustavo) - . Added the classes: IntlCalendar, IntlGregorianCalendar, IntlTimeZone, - IntlBreakIterator, IntlRuleBasedBreakIterator and - IntlCodePointBreakIterator. (Gustavo) - . Added the functions: intlcal_get_keyword_values_for_locale(), - intlcal_get_now(), intlcal_get_available_locales(), intlcal_get(), - intlcal_get_time(), intlcal_set_time(), intlcal_add(), - intlcal_set_time_zone(), intlcal_after(), intlcal_before(), intlcal_set(), - intlcal_roll(), intlcal_clear(), intlcal_field_difference(), - intlcal_get_actual_maximum(), intlcal_get_actual_minimum(), - intlcal_get_day_of_week_type(), intlcal_get_first_day_of_week(), - intlcal_get_greatest_minimum(), intlcal_get_least_maximum(), - intlcal_get_locale(), intlcal_get_maximum(), - intlcal_get_minimal_days_in_first_week(), intlcal_get_minimum(), - intlcal_get_time_zone(), intlcal_get_type(), - intlcal_get_weekend_transition(), intlcal_in_daylight_time(), - intlcal_is_equivalent_to(), intlcal_is_lenient(), intlcal_is_set(), - intlcal_is_weekend(), intlcal_set_first_day_of_week(), - intlcal_set_lenient(), intlcal_equals(), - intlcal_get_repeated_wall_time_option(), - intlcal_get_skipped_wall_time_option(), - intlcal_set_repeated_wall_time_option(), - intlcal_set_skipped_wall_time_option(), intlcal_from_date_time(), - intlcal_to_date_time(), intlcal_get_error_code(), - intlcal_get_error_message(), intlgregcal_create_instance(), - intlgregcal_set_gregorian_change(), intlgregcal_get_gregorian_change() and - intlgregcal_is_leap_year(). (Gustavo) - . Added the functions: intltz_create_time_zone(), intltz_create_default(), - intltz_get_id(), intltz_get_gmt(), intltz_get_unknown(), - intltz_create_enumeration(), intltz_count_equivalent_ids(), - intltz_create_time_zone_id_enumeration(), intltz_get_canonical_id(), - intltz_get_region(), intltz_get_tz_data_version(), - intltz_get_equivalent_id(), intltz_use_daylight_time(), intltz_get_offset(), - intltz_get_raw_offset(), intltz_has_same_rules(), intltz_get_display_name(), - intltz_get_dst_savings(), intltz_from_date_time_zone(), - intltz_to_date_time_zone(), intltz_get_error_code(), - intltz_get_error_message(). (Gustavo) - . Added the methods: IntlDateFormatter::formatObject(), - IntlDateFormatter::getCalendarObject(), IntlDateFormatter::getTimeZone(), - IntlDateFormatter::setTimeZone(). (Gustavo) - . Added the functions: datefmt_format_object(), datefmt_get_calendar_object(), - datefmt_get_timezone(), datefmt_set_timezone(), - datefmt_get_calendar_object(), intlcal_create_instance(). (Gustavo) - -- MCrypt - . mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb() and mcrypt_ofb() now throw - E_DEPRECATED. (GoogleGuy) - -- MySQLi - . Dropped support for LOAD DATA LOCAL INFILE handlers when using libmysql. - Known for stability problems. (Andrey) - . Added support for SHA256 authentication available with MySQL 5.6.6+. - (Andrey) - -- PCRE: - . Deprecated the /e modifier - (https://wiki.php.net/rfc/remove_preg_replace_eval_modifier). (Nikita Popov) - . Fixed bug #63284 (Upgrade PCRE to 8.31). (Anatoliy) - -- pgsql - . Added pg_escape_literal() and pg_escape_identifier() (Yasuo) - -- SPL - . Fix bug #60560 (SplFixedArray un-/serialize, getSize(), count() return 0, - keys are strings). (Adam) - -- Tokenizer: - . Fixed bug #60097 (token_get_all fails to lex nested heredoc). (Nikita Popov) - -- Zip: - . Upgraded libzip to 0.10.1 (Anatoliy) - -- Fileinfo: - . Fixed bug #63248 (Load multiple magic files from a directory under Windows). - (Anatoliy) - -- General improvements: - . Implemented FR #46487 (Dereferencing process-handles no longer waits on - those processes). (Jille Timmermans) - -<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>> diff --git a/README.RELEASE_PROCESS b/README.RELEASE_PROCESS index 2512d3a8b..c8eeb4e13 100644 --- a/README.RELEASE_PROCESS +++ b/README.RELEASE_PROCESS @@ -154,7 +154,15 @@ origin <branch>``". 11. run: ``./makedist php 5.4.1``, this will export the tree, create configure and build two tarballs (one gz and one bz2). -12. Commit those two tarballs to Git (php-distributions.git) +12. Commit those two tarballs to web/php-distributions.git, then update the git + submodule reference in web/php.git: + git submodule init; + git submodule update; + cd distributions; + git pull origin master; + cd ..; + git commit distributions; + git push; 13. Once the release has been tagged, contact the PHP Windows development team (internals-win@lists.php.net) so that Windows binaries can be created. Once @@ -79,6 +79,10 @@ PHP 5.5 UPGRADE NOTES (https://wiki.php.net/rfc/generators) - ClassName::class syntax returning full class name for a class as a string constant. (https://wiki.php.net/rfc/class_name_scalars) +- Support for changing the process's title in CLI/CLI-Server SAPIs. (Keyur) + (https://wiki.php.net/rfc/cli_process_title) +- Added support for non-scalar Iterator keys in foreach. + (https://wiki.php.net/rfc/foreach-non-scalar-keys). ======================================== 2. Changes in SAPI modules diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 44cdfaee6..56243d1f8 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -64,6 +64,14 @@ void zend_qsort_r(void *base, size_t nmemb, size_t siz, compare_r_func_t compare The extra argument it has (relatively to zend_qsort()) is passed to the comparison function. + d. get_current_key + +The signature of the get_current_key iteration handler has been changed to: + +void (*get_current_key)(zend_object_iterator *iter, zval *key TSRMLS_DC); + +The key should be written into the zval* using the ZVAL_* macros. + ======================== 2. Build system changes ======================== diff --git a/Zend/tests/bug55156.phpt b/Zend/tests/bug55156.phpt index 6c0ff768d..7d75ce3e9 100644 --- a/Zend/tests/bug55156.phpt +++ b/Zend/tests/bug55156.phpt @@ -1,5 +1,8 @@ --TEST-- Bug #55156 (ReflectionClass::getDocComment() returns comment even though the class has none) +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- <?php diff --git a/Zend/tests/bug61025.phpt b/Zend/tests/bug61025.phpt new file mode 100644 index 000000000..0709c28fb --- /dev/null +++ b/Zend/tests/bug61025.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #61025 (__invoke() visibility not honored) +--FILE-- +<?php + +Interface InvokeAble { + static function __invoke(); +} + +class Bar { + private function __invoke() { + return __CLASS__; + } +} + +$b = new Bar; +echo $b(); + +echo $b->__invoke(); + +?> +--EXPECTF-- +Warning: The magic method __invoke() must have public visibility and cannot be static in %sbug61025.php on line %d + +Warning: The magic method __invoke() must have public visibility and cannot be static in %sbug61025.php on line %d +Bar +Fatal error: Call to private method Bar::__invoke() from context '' in %sbug61025.php on line %d diff --git a/Zend/tests/bug62343.phpt b/Zend/tests/bug62343.phpt new file mode 100644 index 000000000..b0208c4ac --- /dev/null +++ b/Zend/tests/bug62343.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug #62343 (Show class_alias In get_declared_classes()) +--FILE-- +<?php +class a { } +class_alias("a", "b"); +$c = get_declared_classes(); +var_dump(end($c)); +var_dump(prev($c)); +?> +--EXPECT-- +string(1) "b" +string(1) "a" diff --git a/Zend/tests/bug63976.phpt b/Zend/tests/bug63976.phpt new file mode 100644 index 000000000..0ac09d9b3 --- /dev/null +++ b/Zend/tests/bug63976.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #63976 (Parent class incorrectly using child constant in class property) +--FILE-- +<?php +if (1) { + class Foo { + const TABLE = "foo"; + public $table = self::TABLE; + } +} +if (1) { + class Bar extends Foo { + const TABLE = "bar"; + } +} +$bar = new Bar(); +var_dump($bar->table); +?> +--EXPECT-- +string(3) "foo" diff --git a/Zend/tests/bug64239_1.phpt b/Zend/tests/bug64239_1.phpt new file mode 100644 index 000000000..fe58cbd76 --- /dev/null +++ b/Zend/tests/bug64239_1.phpt @@ -0,0 +1,22 @@ +--TEST-- +Bug #64239 (get_class_methods() changed behavior) +--FILE-- +<?php +class A { + public function test() { $this->backtrace(); } +} +class B { + use T2 { t2method as Bmethod; } +} +trait T2 { + public function t2method() { + } +} +var_dump(get_class_methods("B")); +--EXPECT-- +array(2) { + [0]=> + string(7) "bmethod" + [1]=> + string(8) "t2method" +} diff --git a/Zend/tests/bug64354.phpt b/Zend/tests/bug64354.phpt new file mode 100644 index 000000000..03a4b80b4 --- /dev/null +++ b/Zend/tests/bug64354.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #64354 (Unserialize array of objects whose class can't be autoloaded fail) +--FILE-- +<?php +class B implements Serializable { + public function serialize() { + throw new Exception("serialize"); + return NULL; + } + + public function unserialize($data) { + } +} + +$data = array(new B); + +try { + serialize($data); +} catch (Exception $e) { + var_dump($e->getMessage()); +} +?> +--EXPECTF-- +string(9) "serialize" diff --git a/Zend/tests/bug64417.phpt b/Zend/tests/bug64417.phpt new file mode 100644 index 000000000..f3ef740b4 --- /dev/null +++ b/Zend/tests/bug64417.phpt @@ -0,0 +1,39 @@ +--TEST-- +Bug #64417 (BC break: ArrayAccess::&offsetGet() in a trait causes fatal error) +--FILE-- +<?php +trait aa { + private $container = array(); + public function offsetSet($offset, $value) { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + public function offsetExists($offset) { + return isset($this->container[$offset]); + } + public function offsetUnset($offset) { + unset($this->container[$offset]); + } + public function &offsetGet($offset) { + $result = null; + if (isset($this->container[$offset])) { + $result = &$this->container[$offset]; + } + return $result; + } +} + +class obj implements ArrayAccess { + use aa; +} + +$o = new obj; +$o['x'] = 1; +++$o['x']; +echo $o['x'], "\n"; +--EXPECT-- +2 + diff --git a/Zend/tests/generators/errors/serialize_unserialize_error.phpt b/Zend/tests/generators/errors/serialize_unserialize_error.phpt index a8470b0a6..aa2d4693f 100644 --- a/Zend/tests/generators/errors/serialize_unserialize_error.phpt +++ b/Zend/tests/generators/errors/serialize_unserialize_error.phpt @@ -38,8 +38,6 @@ Stack trace: #1 %s(%d): unserialize('O:9:"Generator"...') #2 {main} - -Notice: unserialize(): Error at offset 19 of 20 bytes in %s on line %d exception 'Exception' with message 'Unserialization of 'Generator' is not allowed' in %s:%d Stack trace: #0 %s(%d): unserialize('C:9:"Generator"...') diff --git a/Zend/tests/generators/generator_with_nonscalar_keys.phpt b/Zend/tests/generators/generator_with_nonscalar_keys.phpt new file mode 100644 index 000000000..5ae55a1be --- /dev/null +++ b/Zend/tests/generators/generator_with_nonscalar_keys.phpt @@ -0,0 +1,52 @@ +--TEST-- +Generators can return non-scalar keys +--FILE-- +<?php + +function gen() { + yield [1, 2, 3] => [4, 5, 6]; + yield (object) ['a' => 'b'] => (object) ['b' => 'a']; + yield 3.14 => 2.73; + yield false => true; + yield true => false; + yield null => null; +} + +foreach (gen() as $k => $v) { + var_dump($k, $v); +} + +?> +--EXPECT-- +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(6) +} +object(stdClass)#3 (1) { + ["a"]=> + string(1) "b" +} +object(stdClass)#4 (1) { + ["b"]=> + string(1) "a" +} +float(3.14) +float(2.73) +bool(false) +bool(true) +bool(true) +bool(false) +NULL +NULL diff --git a/Zend/tests/ns_026.phpt b/Zend/tests/ns_026.phpt index af2bf2ca5..45f88750e 100644 --- a/Zend/tests/ns_026.phpt +++ b/Zend/tests/ns_026.phpt @@ -1,5 +1,7 @@ --TEST-- 026: Name ambiguity (class name & namespace name) +--INI-- +opcache.optimization_level=0 --FILE-- <?php namespace Foo; diff --git a/Zend/zend.h b/Zend/zend.h index aed03d871..36554637e 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -299,7 +299,6 @@ void zend_error_noreturn(int type, const char *format, ...) __attribute__ ((nore /* * zval */ -typedef struct _zval_struct zval; typedef struct _zend_class_entry zend_class_entry; typedef struct _zend_guard { @@ -365,6 +364,10 @@ struct _zval_struct { #define Z_UNSET_ISREF(z) Z_UNSET_ISREF_P(&(z)) #define Z_SET_ISREF_TO(z, isref) Z_SET_ISREF_TO_P(&(z), isref) +#if ZEND_DEBUG +#define zend_always_inline inline +#define zend_never_inline +#else #if defined(__GNUC__) #if __GNUC__ >= 3 #define zend_always_inline inline __attribute__((always_inline)) @@ -373,7 +376,6 @@ struct _zval_struct { #define zend_always_inline inline #define zend_never_inline #endif - #elif defined(_MSC_VER) #define zend_always_inline __forceinline #define zend_never_inline @@ -381,6 +383,7 @@ struct _zval_struct { #define zend_always_inline inline #define zend_never_inline #endif +#endif /* ZEND_DEBUG */ #if (defined (__GNUC__) && __GNUC__ > 2 ) && !defined(DARWIN) && !defined(__hpux) && !defined(_AIX) # define EXPECTED(condition) __builtin_expect(condition, 1) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 95c90ea75..1f400dea1 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1051,6 +1051,41 @@ ZEND_API void zend_merge_properties(zval *obj, HashTable *properties, int destro } /* }}} */ +static int zval_update_class_constant(zval **pp, int is_static, int offset TSRMLS_DC) /* {{{ */ +{ + if ((Z_TYPE_PP(pp) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT || + (Z_TYPE_PP(pp) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT_ARRAY) { + zend_class_entry **scope = EG(in_execution)?&EG(scope):&CG(active_class_entry); + + if ((*scope)->parent) { + zend_class_entry *ce = *scope; + HashPosition pos; + zend_property_info *prop_info; + + do { + for (zend_hash_internal_pointer_reset_ex(&ce->properties_info, &pos); + zend_hash_get_current_data_ex(&ce->properties_info, (void **) &prop_info, &pos) == SUCCESS; + zend_hash_move_forward_ex(&ce->properties_info, &pos)) { + if (is_static == ((prop_info->flags & ZEND_ACC_STATIC) != 0) && + offset == prop_info->offset) { + int ret; + zend_class_entry *old_scope = *scope; + *scope = prop_info->ce; + ret = zval_update_constant(pp, (void*)1 TSRMLS_CC); + *scope = old_scope; + return ret; + } + } + ce = ce->parent; + } while (ce); + + } + return zval_update_constant(pp, (void*)1 TSRMLS_CC); + } + return 0; +} +/* }}} */ + ZEND_API void zend_update_class_constants(zend_class_entry *class_type TSRMLS_DC) /* {{{ */ { if ((class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) == 0 || (!CE_STATIC_MEMBERS(class_type) && class_type->default_static_members_count)) { @@ -1063,7 +1098,7 @@ ZEND_API void zend_update_class_constants(zend_class_entry *class_type TSRMLS_DC for (i = 0; i < class_type->default_properties_count; i++) { if (class_type->default_properties_table[i]) { - zval_update_constant(&class_type->default_properties_table[i], (void**)1 TSRMLS_CC); + zval_update_class_constant(&class_type->default_properties_table[i], 0, i TSRMLS_CC); } } @@ -1104,7 +1139,7 @@ ZEND_API void zend_update_class_constants(zend_class_entry *class_type TSRMLS_DC } for (i = 0; i < class_type->default_static_members_count; i++) { - zval_update_constant(&CE_STATIC_MEMBERS(class_type)[i], (void**)1 TSRMLS_CC); + zval_update_class_constant(&CE_STATIC_MEMBERS(class_type)[i], 1, i TSRMLS_CC); } *scope = old_scope; @@ -1502,6 +1537,40 @@ ZEND_API int add_get_index_stringl(zval *arg, ulong index, const char *str, uint } /* }}} */ +ZEND_API int array_set_zval_key(HashTable *ht, zval *key, zval *value) /* {{{ */ +{ + int result; + + switch (Z_TYPE_P(key)) { + case IS_STRING: + result = zend_symtable_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL); + break; + case IS_NULL: + result = zend_symtable_update(ht, "", 1, &value, sizeof(zval *), NULL); + break; + case IS_RESOURCE: + zend_error(E_STRICT, "Resource ID#%ld used as offset, casting to integer (%ld)", Z_LVAL_P(key), Z_LVAL_P(key)); + /* break missing intentionally */ + case IS_BOOL: + case IS_LONG: + result = zend_hash_index_update(ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL); + break; + case IS_DOUBLE: + result = zend_hash_index_update(ht, zend_dval_to_lval(Z_DVAL_P(key)), &value, sizeof(zval *), NULL); + break; + default: + zend_error(E_WARNING, "Illegal offset type"); + result = FAILURE; + } + + if (result == SUCCESS) { + Z_ADDREF_P(value); + } + + return result; +} +/* }}} */ + ZEND_API int add_property_long_ex(zval *arg, const char *key, uint key_len, long n TSRMLS_DC) /* {{{ */ { zval *tmp; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index fb642c147..c26141b18 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -426,6 +426,8 @@ ZEND_API int add_get_index_double(zval *arg, ulong idx, double d, void **dest); ZEND_API int add_get_index_string(zval *arg, ulong idx, const char *str, void **dest, int duplicate); ZEND_API int add_get_index_stringl(zval *arg, ulong idx, const char *str, uint length, void **dest, int duplicate); +ZEND_API int array_set_zval_key(HashTable *ht, zval *key, zval *value); + ZEND_API int add_property_long_ex(zval *arg, const char *key, uint key_len, long l TSRMLS_DC); ZEND_API int add_property_null_ex(zval *arg, const char *key, uint key_len TSRMLS_DC); ZEND_API int add_property_bool_ex(zval *arg, const char *key, uint key_len, int b TSRMLS_DC); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 4d950a203..ee75f521f 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1025,6 +1025,13 @@ ZEND_FUNCTION(get_object_vars) } /* }}} */ +static int same_name(const char *key, const char *name, zend_uint name_len) +{ + char *lcname = zend_str_tolower_dup(name, name_len); + int ret = memcmp(lcname, key, name_len) == 0; + efree(lcname); + return ret; +} /* {{{ proto array get_class_methods(mixed class) Returns an array of method names for class or class instance. */ @@ -1072,14 +1079,26 @@ ZEND_FUNCTION(get_class_methods) uint len = strlen(mptr->common.function_name); /* Do not display old-style inherited constructors */ - if ((mptr->common.fn_flags & ZEND_ACC_CTOR) == 0 || - mptr->common.scope == ce || - zend_hash_get_current_key_ex(&ce->function_table, &key, &key_len, &num_index, 0, &pos) != HASH_KEY_IS_STRING || - zend_binary_strcasecmp(key, key_len-1, mptr->common.function_name, len) == 0) { - + if (zend_hash_get_current_key_ex(&ce->function_table, &key, &key_len, &num_index, 0, &pos) != HASH_KEY_IS_STRING) { MAKE_STD_ZVAL(method_name); ZVAL_STRINGL(method_name, mptr->common.function_name, len, 1); zend_hash_next_index_insert(return_value->value.ht, &method_name, sizeof(zval *), NULL); + } else if ((mptr->common.fn_flags & ZEND_ACC_CTOR) == 0 || + mptr->common.scope == ce || + zend_binary_strcasecmp(key, key_len-1, mptr->common.function_name, len) == 0) { + + if (mptr->type == ZEND_USER_FUNCTION && + *mptr->op_array.refcount > 1 && + (len != key_len - 1 || + !same_name(key, mptr->common.function_name, len))) { + MAKE_STD_ZVAL(method_name); + ZVAL_STRINGL(method_name, key, key_len - 1, 1); + zend_hash_next_index_insert(return_value->value.ht, &method_name, sizeof(zval *), NULL); + } else { + MAKE_STD_ZVAL(method_name); + ZVAL_STRINGL(method_name, mptr->common.function_name, len, 1); + zend_hash_next_index_insert(return_value->value.ht, &method_name, sizeof(zval *), NULL); + } } } zend_hash_move_forward_ex(&ce->function_table, &pos); @@ -1625,7 +1644,6 @@ ZEND_FUNCTION(restore_exception_handler) } /* }}} */ - static int copy_class_or_interface_name(zend_class_entry **pce TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) { zval *array = va_arg(args, zval *); @@ -1636,7 +1654,13 @@ static int copy_class_or_interface_name(zend_class_entry **pce TSRMLS_DC, int nu if ((hash_key->nKeyLength==0 || hash_key->arKey[0]!=0) && (comply_mask == (ce->ce_flags & mask))) { - add_next_index_stringl(array, ce->name, ce->name_length, 1); + if (ce->refcount > 1 && + (ce->name_length != hash_key->nKeyLength - 1 || + !same_name(hash_key->arKey, ce->name, ce->name_length))) { + add_next_index_stringl(array, hash_key->arKey, hash_key->nKeyLength - 1, 1); + } else { + add_next_index_stringl(array, ce->name, ce->name_length, 1); + } } return ZEND_HASH_APPLY_KEEP; } diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 0de928333..5faefbd22 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -291,7 +291,6 @@ static zend_object_value zend_closure_clone(zval *zobject TSRMLS_DC) /* {{{ */ } /* }}} */ - int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) /* {{{ */ { zend_closure *closure; diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h index c41cf4756..9d50641d2 100644 --- a/Zend/zend_closures.h +++ b/Zend/zend_closures.h @@ -24,8 +24,6 @@ BEGIN_EXTERN_C() -#define ZEND_INVOKE_FUNC_NAME "__invoke" - void zend_register_closure_ce(TSRMLS_D); extern ZEND_API zend_class_entry *zend_ce_closure; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e45419d3f..6df3defb1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1621,6 +1621,10 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { zend_error(E_WARNING, "The magic method __toString() must have public visibility and cannot be static"); } + } else if ((name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1))) { + if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { + zend_error(E_WARNING, "The magic method __invoke() must have public visibility and cannot be static"); + } } } else { char *class_lcname; @@ -1677,6 +1681,10 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n zend_error(E_WARNING, "The magic method __toString() must have public visibility and cannot be static"); } CG(active_class_entry)->__tostring = (zend_function *) CG(active_op_array); + } else if ((name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1))) { + if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { + zend_error(E_WARNING, "The magic method __invoke() must have public visibility and cannot be static"); + } } else if (!(fn_flags & ZEND_ACC_STATIC)) { CG(active_op_array)->fn_flags |= ZEND_ACC_ALLOW_STATIC; } @@ -3823,7 +3831,7 @@ static zend_bool zend_traits_method_compatibility_check(zend_function *fn, zend_ zend_uint other_flags = other_fn->common.scope->ce_flags; return zend_do_perform_implementation_check(fn, other_fn TSRMLS_CC) - && zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC) + && ((other_fn->common.scope->ce_flags & ZEND_ACC_INTERFACE) || zend_do_perform_implementation_check(other_fn, fn TSRMLS_CC)) && ((fn_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC)) == (other_flags & (ZEND_ACC_FINAL|ZEND_ACC_STATIC))); /* equal final and static qualifier */ } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8042dd54e..2295cffab 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -856,6 +856,7 @@ END_EXTERN_C() #define ZEND_CALLSTATIC_FUNC_NAME "__callstatic" #define ZEND_TOSTRING_FUNC_NAME "__tostring" #define ZEND_AUTOLOAD_FUNC_NAME "__autoload" +#define ZEND_INVOKE_FUNC_NAME "__invoke" /* The following constants may be combined in CG(compiler_options) * to change the default compiler behavior */ diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index c1dbee124..3f43552f1 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -755,31 +755,17 @@ static void zend_generator_iterator_get_data(zend_object_iterator *iterator, zva } /* }}} */ -static int zend_generator_iterator_get_key(zend_object_iterator *iterator, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */ +static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval *key TSRMLS_DC) /* {{{ */ { zend_generator *generator = (zend_generator *) iterator->data; zend_generator_ensure_initialized(generator TSRMLS_CC); - if (!generator->key) { - return HASH_KEY_NON_EXISTANT; - } - - if (Z_TYPE_P(generator->key) == IS_LONG) { - *int_key = Z_LVAL_P(generator->key); - return HASH_KEY_IS_LONG; - } - - if (Z_TYPE_P(generator->key) == IS_STRING) { - *str_key = estrndup(Z_STRVAL_P(generator->key), Z_STRLEN_P(generator->key)); - *str_key_len = Z_STRLEN_P(generator->key) + 1; - return HASH_KEY_IS_STRING; + if (generator->key) { + ZVAL_ZVAL(key, generator->key, 1, 0); + } else { + ZVAL_NULL(key); } - - /* Waiting for Etienne's patch to allow arbitrary zval keys. Until then - * error out on non-int and non-string keys. */ - zend_error_noreturn(E_ERROR, "Currently only int and string keys can be yielded"); - return HASH_KEY_NON_EXISTANT; /* Nerver reached */ } /* }}} */ diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 0609d707f..bca47b330 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -1171,6 +1171,24 @@ ZEND_API int zend_hash_get_current_key_ex(const HashTable *ht, char **str_index, return HASH_KEY_NON_EXISTANT; } +ZEND_API void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) { + Bucket *p; + + IS_CONSISTENT(ht); + + p = pos ? (*pos) : ht->pInternalPointer; + + if (!p) { + Z_TYPE_P(key) = IS_NULL; + } else if (p->nKeyLength) { + Z_TYPE_P(key) = IS_STRING; + Z_STRVAL_P(key) = IS_INTERNED(p->arKey) ? (char *) p->arKey : estrndup(p->arKey, p->nKeyLength - 1); + Z_STRLEN_P(key) = p->nKeyLength - 1; + } else { + Z_TYPE_P(key) = IS_LONG; + Z_LVAL_P(key) = p->h; + } +} ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos) { diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 88c3bfb42..a0c147f39 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -170,13 +170,13 @@ ZEND_API int zend_hash_quick_exists(const HashTable *ht, const char *arKey, uint ZEND_API int zend_hash_index_exists(const HashTable *ht, ulong h); ZEND_API ulong zend_hash_next_free_element(const HashTable *ht); - /* traversing */ #define zend_hash_has_more_elements_ex(ht, pos) \ (zend_hash_get_current_key_type_ex(ht, pos) == HASH_KEY_NON_EXISTANT ? FAILURE : SUCCESS) ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos); ZEND_API int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos); ZEND_API int zend_hash_get_current_key_ex(const HashTable *ht, char **str_index, uint *str_length, ulong *num_index, zend_bool duplicate, HashPosition *pos); +ZEND_API void zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos); ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos); ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos); ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos); @@ -199,6 +199,8 @@ ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr); zend_hash_move_backwards_ex(ht, NULL) #define zend_hash_get_current_key(ht, str_index, num_index, duplicate) \ zend_hash_get_current_key_ex(ht, str_index, NULL, num_index, duplicate, NULL) +#define zend_hash_get_current_key_zval(ht, key) \ + zend_hash_get_current_key_zval_ex(ht, key, NULL) #define zend_hash_get_current_key_type(ht) \ zend_hash_get_current_key_type_ex(ht, NULL) #define zend_hash_get_current_data(ht, pData) \ diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index 384b66da4..16751549b 100644 --- a/Zend/zend_interfaces.c +++ b/Zend/zend_interfaces.c @@ -195,7 +195,7 @@ static int zend_user_it_get_current_key_default(zend_object_iterator *_iter, cha /* }}} */ /* {{{ zend_user_it_get_current_key */ -ZEND_API int zend_user_it_get_current_key(zend_object_iterator *_iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) +ZEND_API void zend_user_it_get_current_key(zend_object_iterator *_iter, zval *key TSRMLS_DC) { zend_user_iterator *iter = (zend_user_iterator*)_iter; zval *object = (zval*)iter->it.data; @@ -203,42 +203,16 @@ ZEND_API int zend_user_it_get_current_key(zend_object_iterator *_iter, char **st zend_call_method_with_0_params(&object, iter->ce, &iter->ce->iterator_funcs.zf_key, "key", &retval); - if (!retval) { - *int_key = 0; - if (!EG(exception)) - { + if (retval) { + ZVAL_ZVAL(key, retval, 1, 1); + } else { + if (!EG(exception)) { zend_error(E_WARNING, "Nothing returned from %s::key()", iter->ce->name); } - return HASH_KEY_IS_LONG; - } - switch (Z_TYPE_P(retval)) { - default: - zend_error(E_WARNING, "Illegal type returned from %s::key()", iter->ce->name); - case IS_NULL: - *int_key = 0; - zval_ptr_dtor(&retval); - return HASH_KEY_IS_LONG; - case IS_STRING: - *str_key = estrndup(Z_STRVAL_P(retval), Z_STRLEN_P(retval)); - *str_key_len = Z_STRLEN_P(retval)+1; - zval_ptr_dtor(&retval); - return HASH_KEY_IS_STRING; - - case IS_DOUBLE: - *int_key = (long)Z_DVAL_P(retval); - zval_ptr_dtor(&retval); - return HASH_KEY_IS_LONG; - - case IS_RESOURCE: - case IS_BOOL: - case IS_LONG: - *int_key = (long)Z_LVAL_P(retval); - zval_ptr_dtor(&retval); - return HASH_KEY_IS_LONG; + ZVAL_LONG(key, 0); } } -/* }}} */ /* {{{ zend_user_it_move_forward */ ZEND_API void zend_user_it_move_forward(zend_object_iterator *_iter TSRMLS_DC) @@ -452,7 +426,7 @@ ZEND_API int zend_user_serialize(zval *object, unsigned char **buffer, zend_uint zval_ptr_dtor(&retval); } - if (result == FAILURE) { + if (result == FAILURE && !EG(exception)) { zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "%s::serialize() must return a string or NULL", ce->name); } return result; diff --git a/Zend/zend_interfaces.h b/Zend/zend_interfaces.h index 23547951e..ba4bc6ccb 100644 --- a/Zend/zend_interfaces.h +++ b/Zend/zend_interfaces.h @@ -51,7 +51,7 @@ ZEND_API zval* zend_call_method(zval **object_pp, zend_class_entry *obj_ce, zend ZEND_API void zend_user_it_rewind(zend_object_iterator *_iter TSRMLS_DC); ZEND_API int zend_user_it_valid(zend_object_iterator *_iter TSRMLS_DC); -ZEND_API int zend_user_it_get_current_key(zend_object_iterator *_iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC); +ZEND_API void zend_user_it_get_current_key(zend_object_iterator *_iter, zval *key TSRMLS_DC); ZEND_API void zend_user_it_get_current_data(zend_object_iterator *_iter, zval ***data TSRMLS_DC); ZEND_API void zend_user_it_move_forward(zend_object_iterator *_iter TSRMLS_DC); ZEND_API void zend_user_it_invalidate_current(zend_object_iterator *_iter TSRMLS_DC); diff --git a/Zend/zend_iterators.h b/Zend/zend_iterators.h index b484102b2..f74068a27 100644 --- a/Zend/zend_iterators.h +++ b/Zend/zend_iterators.h @@ -38,8 +38,11 @@ typedef struct _zend_object_iterator_funcs { /* fetch the item data for the current element */ void (*get_current_data)(zend_object_iterator *iter, zval ***data TSRMLS_DC); - /* fetch the key for the current element (return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG) (optional, may be NULL) */ - int (*get_current_key)(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC); + /* fetch the key for the current element (optional, may be NULL). The key + * should be written into the provided zval* using the ZVAL_* macros. If + * this handler is not provided auto-incrementing integer keys will be + * used. */ + void (*get_current_key)(zend_object_iterator *iter, zval *key TSRMLS_DC); /* step forwards to next element */ void (*move_forward)(zend_object_iterator *iter TSRMLS_DC); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 3e68add3d..9cdf31fb3 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -52,6 +52,7 @@ typedef unsigned long zend_uintptr_t; typedef unsigned int zend_object_handle; typedef struct _zend_object_handlers zend_object_handlers; +typedef struct _zval_struct zval; typedef struct _zend_object_value { zend_object_handle handle; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 578a319fd..206a2333f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2839,17 +2839,17 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) if (OP1_TYPE == IS_TMP_VAR) { FREE_OP1(); } - } else if (!IS_OP1_TMP_FREE()) { /* Not a temp var */ - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } + } else { if (OP1_TYPE == IS_CONST || - (PZVAL_IS_REF(retval_ptr) && Z_REFCOUNT_P(retval_ptr) > 0)) { + OP1_TYPE == IS_TMP_VAR || + PZVAL_IS_REF(retval_ptr)) { zval *ret; ALLOC_ZVAL(ret); INIT_PZVAL_COPY(ret, retval_ptr); - zval_copy_ctor(ret); + if (OP1_TYPE != IS_TMP_VAR) { + zval_copy_ctor(ret); + } *EG(return_value_ptr_ptr) = ret; } else if ((OP1_TYPE == IS_CV || OP1_TYPE == IS_VAR) && retval_ptr == &EG(uninitialized_zval)) { @@ -2861,16 +2861,6 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) *EG(return_value_ptr_ptr) = retval_ptr; Z_ADDREF_P(retval_ptr); } - } else { - zval *ret; - - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - - ALLOC_ZVAL(ret); - INIT_PZVAL_COPY(ret, retval_ptr); - *EG(return_value_ptr_ptr) = ret; } FREE_OP1_IF_VAR(); ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); @@ -2886,10 +2876,6 @@ ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY) SAVE_OPLINE(); do { - if (EG(return_value_ptr_ptr) && *EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR) { /* Not supposed to happen, but we'll allow it */ zend_error(E_NOTICE, "Only variable references should be returned by reference"); @@ -4245,13 +4231,13 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) zend_free_op free_op1; zval *array = EX_T(opline->op1.var).fe.ptr; zval **value; - char *str_key; - uint str_key_len; - ulong int_key; HashTable *fe_ht; zend_object_iterator *iter = NULL; - int key_type = 0; - zend_bool use_key = (zend_bool)(opline->extended_value & ZEND_FE_FETCH_WITH_KEY); + + zval *key = NULL; + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + key = &EX_T((opline+1)->result.var).tmp_var; + } SAVE_OPLINE(); @@ -4262,8 +4248,11 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.opline_num); case ZEND_ITER_PLAIN_OBJECT: { - const char *class_name, *prop_name; zend_object *zobj = zend_objects_get_address(array TSRMLS_CC); + int key_type; + char *str_key; + zend_uint str_key_len; + zend_ulong int_key; fe_ht = Z_OBJPROP_P(array); zend_hash_set_pointer(fe_ht, &EX_T(opline->op1.var).fe.fe_pos); @@ -4275,15 +4264,23 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) key_type = zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 0, NULL); zend_hash_move_forward(fe_ht); - } while (key_type == HASH_KEY_NON_EXISTANT || - (key_type != HASH_KEY_IS_LONG && - zend_check_property_access(zobj, str_key, str_key_len-1 TSRMLS_CC) != SUCCESS)); - zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.var).fe.fe_pos); - if (use_key && key_type != HASH_KEY_IS_LONG) { - zend_unmangle_property_name_ex(str_key, str_key_len-1, &class_name, &prop_name, &str_key_len); - str_key = estrndup(prop_name, str_key_len); - str_key_len++; + } while (key_type != HASH_KEY_IS_LONG && + zend_check_property_access(zobj, str_key, str_key_len - 1 TSRMLS_CC) != SUCCESS); + + if (key) { + if (key_type == HASH_KEY_IS_LONG) { + ZVAL_LONG(key, int_key); + } else { + const char *class_name, *prop_name; + int prop_name_len; + zend_unmangle_property_name_ex( + str_key, str_key_len - 1, &class_name, &prop_name, &prop_name_len + ); + ZVAL_STRINGL(key, prop_name, prop_name_len, 1); + } } + + zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.var).fe.fe_pos); break; } @@ -4294,8 +4291,8 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) /* reached end of iteration */ ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.opline_num); } - if (use_key) { - key_type = zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL); + if (key) { + zend_hash_get_current_key_zval(fe_ht, key); } zend_hash_move_forward(fe_ht); zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.var).fe.fe_pos); @@ -4330,16 +4327,15 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) /* failure in get_current_data */ ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.opline_num); } - if (use_key) { + if (key) { if (iter->funcs->get_current_key) { - key_type = iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC); + iter->funcs->get_current_key(iter, key TSRMLS_CC); if (UNEXPECTED(EG(exception) != NULL)) { zval_ptr_dtor(&array); HANDLE_EXCEPTION(); } } else { - key_type = HASH_KEY_IS_LONG; - int_key = iter->index; + ZVAL_LONG(key, iter->index); } } break; @@ -4355,26 +4351,6 @@ ZEND_VM_HANDLER(78, ZEND_FE_FETCH, VAR, ANY) AI_SET_PTR(&EX_T(opline->result.var), *value); } - if (use_key) { - zval *key = &EX_T((opline+1)->result.var).tmp_var; - - switch (key_type) { - case HASH_KEY_IS_STRING: - Z_STRVAL_P(key) = (char*)str_key; - Z_STRLEN_P(key) = str_key_len-1; - Z_TYPE_P(key) = IS_STRING; - break; - case HASH_KEY_IS_LONG: - Z_LVAL_P(key) = int_key; - Z_TYPE_P(key) = IS_LONG; - break; - default: - case HASH_KEY_NON_EXISTANT: - ZVAL_NULL(key); - break; - } - } - CHECK_EXCEPTION(); ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 392569678..d65dfc41f 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2333,17 +2333,17 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARG if (IS_CONST == IS_TMP_VAR) { } - } else if (!0) { /* Not a temp var */ - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } + } else { if (IS_CONST == IS_CONST || - (PZVAL_IS_REF(retval_ptr) && Z_REFCOUNT_P(retval_ptr) > 0)) { + IS_CONST == IS_TMP_VAR || + PZVAL_IS_REF(retval_ptr)) { zval *ret; ALLOC_ZVAL(ret); INIT_PZVAL_COPY(ret, retval_ptr); - zval_copy_ctor(ret); + if (IS_CONST != IS_TMP_VAR) { + zval_copy_ctor(ret); + } *EG(return_value_ptr_ptr) = ret; } else if ((IS_CONST == IS_CV || IS_CONST == IS_VAR) && retval_ptr == &EG(uninitialized_zval)) { @@ -2355,16 +2355,6 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARG *EG(return_value_ptr_ptr) = retval_ptr; Z_ADDREF_P(retval_ptr); } - } else { - zval *ret; - - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - - ALLOC_ZVAL(ret); - INIT_PZVAL_COPY(ret, retval_ptr); - *EG(return_value_ptr_ptr) = ret; } return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -2380,10 +2370,6 @@ static int ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND SAVE_OPLINE(); do { - if (EG(return_value_ptr_ptr) && *EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { /* Not supposed to happen, but we'll allow it */ zend_error(E_NOTICE, "Only variable references should be returned by reference"); @@ -7654,17 +7640,17 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (IS_TMP_VAR == IS_TMP_VAR) { zval_dtor(free_op1.var); } - } else if (!1) { /* Not a temp var */ - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } + } else { if (IS_TMP_VAR == IS_CONST || - (PZVAL_IS_REF(retval_ptr) && Z_REFCOUNT_P(retval_ptr) > 0)) { + IS_TMP_VAR == IS_TMP_VAR || + PZVAL_IS_REF(retval_ptr)) { zval *ret; ALLOC_ZVAL(ret); INIT_PZVAL_COPY(ret, retval_ptr); - zval_copy_ctor(ret); + if (IS_TMP_VAR != IS_TMP_VAR) { + zval_copy_ctor(ret); + } *EG(return_value_ptr_ptr) = ret; } else if ((IS_TMP_VAR == IS_CV || IS_TMP_VAR == IS_VAR) && retval_ptr == &EG(uninitialized_zval)) { @@ -7676,16 +7662,6 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) *EG(return_value_ptr_ptr) = retval_ptr; Z_ADDREF_P(retval_ptr); } - } else { - zval *ret; - - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - - ALLOC_ZVAL(ret); - INIT_PZVAL_COPY(ret, retval_ptr); - *EG(return_value_ptr_ptr) = ret; } return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -7701,10 +7677,6 @@ static int ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLE SAVE_OPLINE(); do { - if (EG(return_value_ptr_ptr) && *EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { /* Not supposed to happen, but we'll allow it */ zend_error(E_NOTICE, "Only variable references should be returned by reference"); @@ -12888,17 +12860,17 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (IS_VAR == IS_TMP_VAR) { if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; } - } else if (!0) { /* Not a temp var */ - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } + } else { if (IS_VAR == IS_CONST || - (PZVAL_IS_REF(retval_ptr) && Z_REFCOUNT_P(retval_ptr) > 0)) { + IS_VAR == IS_TMP_VAR || + PZVAL_IS_REF(retval_ptr)) { zval *ret; ALLOC_ZVAL(ret); INIT_PZVAL_COPY(ret, retval_ptr); - zval_copy_ctor(ret); + if (IS_VAR != IS_TMP_VAR) { + zval_copy_ctor(ret); + } *EG(return_value_ptr_ptr) = ret; } else if ((IS_VAR == IS_CV || IS_VAR == IS_VAR) && retval_ptr == &EG(uninitialized_zval)) { @@ -12910,16 +12882,6 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) *EG(return_value_ptr_ptr) = retval_ptr; Z_ADDREF_P(retval_ptr); } - } else { - zval *ret; - - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - - ALLOC_ZVAL(ret); - INIT_PZVAL_COPY(ret, retval_ptr); - *EG(return_value_ptr_ptr) = ret; } if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -12935,10 +12897,6 @@ static int ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLE SAVE_OPLINE(); do { - if (EG(return_value_ptr_ptr) && *EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { /* Not supposed to happen, but we'll allow it */ zend_error(E_NOTICE, "Only variable references should be returned by reference"); @@ -13588,13 +13546,13 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG zval *array = EX_T(opline->op1.var).fe.ptr; zval **value; - char *str_key; - uint str_key_len; - ulong int_key; HashTable *fe_ht; zend_object_iterator *iter = NULL; - int key_type = 0; - zend_bool use_key = (zend_bool)(opline->extended_value & ZEND_FE_FETCH_WITH_KEY); + + zval *key = NULL; + if (opline->extended_value & ZEND_FE_FETCH_WITH_KEY) { + key = &EX_T((opline+1)->result.var).tmp_var; + } SAVE_OPLINE(); @@ -13605,8 +13563,11 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.opline_num); case ZEND_ITER_PLAIN_OBJECT: { - const char *class_name, *prop_name; zend_object *zobj = zend_objects_get_address(array TSRMLS_CC); + int key_type; + char *str_key; + zend_uint str_key_len; + zend_ulong int_key; fe_ht = Z_OBJPROP_P(array); zend_hash_set_pointer(fe_ht, &EX_T(opline->op1.var).fe.fe_pos); @@ -13618,15 +13579,23 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG key_type = zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 0, NULL); zend_hash_move_forward(fe_ht); - } while (key_type == HASH_KEY_NON_EXISTANT || - (key_type != HASH_KEY_IS_LONG && - zend_check_property_access(zobj, str_key, str_key_len-1 TSRMLS_CC) != SUCCESS)); - zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.var).fe.fe_pos); - if (use_key && key_type != HASH_KEY_IS_LONG) { - zend_unmangle_property_name_ex(str_key, str_key_len-1, &class_name, &prop_name, &str_key_len); - str_key = estrndup(prop_name, str_key_len); - str_key_len++; + } while (key_type != HASH_KEY_IS_LONG && + zend_check_property_access(zobj, str_key, str_key_len - 1 TSRMLS_CC) != SUCCESS); + + if (key) { + if (key_type == HASH_KEY_IS_LONG) { + ZVAL_LONG(key, int_key); + } else { + const char *class_name, *prop_name; + int prop_name_len; + zend_unmangle_property_name_ex( + str_key, str_key_len - 1, &class_name, &prop_name, &prop_name_len + ); + ZVAL_STRINGL(key, prop_name, prop_name_len, 1); + } } + + zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.var).fe.fe_pos); break; } @@ -13637,8 +13606,8 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG /* reached end of iteration */ ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.opline_num); } - if (use_key) { - key_type = zend_hash_get_current_key_ex(fe_ht, &str_key, &str_key_len, &int_key, 1, NULL); + if (key) { + zend_hash_get_current_key_zval(fe_ht, key); } zend_hash_move_forward(fe_ht); zend_hash_get_pointer(fe_ht, &EX_T(opline->op1.var).fe.fe_pos); @@ -13673,16 +13642,15 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG /* failure in get_current_data */ ZEND_VM_JMP(EX(op_array)->opcodes+opline->op2.opline_num); } - if (use_key) { + if (key) { if (iter->funcs->get_current_key) { - key_type = iter->funcs->get_current_key(iter, &str_key, &str_key_len, &int_key TSRMLS_CC); + iter->funcs->get_current_key(iter, key TSRMLS_CC); if (UNEXPECTED(EG(exception) != NULL)) { zval_ptr_dtor(&array); HANDLE_EXCEPTION(); } } else { - key_type = HASH_KEY_IS_LONG; - int_key = iter->index; + ZVAL_LONG(key, iter->index); } } break; @@ -13698,26 +13666,6 @@ static int ZEND_FASTCALL ZEND_FE_FETCH_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG AI_SET_PTR(&EX_T(opline->result.var), *value); } - if (use_key) { - zval *key = &EX_T((opline+1)->result.var).tmp_var; - - switch (key_type) { - case HASH_KEY_IS_STRING: - Z_STRVAL_P(key) = (char*)str_key; - Z_STRLEN_P(key) = str_key_len-1; - Z_TYPE_P(key) = IS_STRING; - break; - case HASH_KEY_IS_LONG: - Z_LVAL_P(key) = int_key; - Z_TYPE_P(key) = IS_LONG; - break; - default: - case HASH_KEY_NON_EXISTANT: - ZVAL_NULL(key); - break; - } - } - CHECK_EXCEPTION(); ZEND_VM_INC_OPCODE(); ZEND_VM_NEXT_OPCODE(); @@ -30534,17 +30482,17 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (IS_CV == IS_TMP_VAR) { } - } else if (!0) { /* Not a temp var */ - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } + } else { if (IS_CV == IS_CONST || - (PZVAL_IS_REF(retval_ptr) && Z_REFCOUNT_P(retval_ptr) > 0)) { + IS_CV == IS_TMP_VAR || + PZVAL_IS_REF(retval_ptr)) { zval *ret; ALLOC_ZVAL(ret); INIT_PZVAL_COPY(ret, retval_ptr); - zval_copy_ctor(ret); + if (IS_CV != IS_TMP_VAR) { + zval_copy_ctor(ret); + } *EG(return_value_ptr_ptr) = ret; } else if ((IS_CV == IS_CV || IS_CV == IS_VAR) && retval_ptr == &EG(uninitialized_zval)) { @@ -30556,16 +30504,6 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) *EG(return_value_ptr_ptr) = retval_ptr; Z_ADDREF_P(retval_ptr); } - } else { - zval *ret; - - if (*EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - - ALLOC_ZVAL(ret); - INIT_PZVAL_COPY(ret, retval_ptr); - *EG(return_value_ptr_ptr) = ret; } return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); @@ -30581,10 +30519,6 @@ static int ZEND_FASTCALL ZEND_RETURN_BY_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER SAVE_OPLINE(); do { - if (EG(return_value_ptr_ptr) && *EG(return_value_ptr_ptr)) { - zval_ptr_dtor(EG(return_value_ptr_ptr)); - } - if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { /* Not supposed to happen, but we'll allow it */ zend_error(E_NOTICE, "Only variable references should be returned by reference"); @@ -1048,6 +1048,7 @@ with_iodbc with_esoob with_unixODBC with_dbmaker +enable_opcache enable_pcntl enable_pdo with_pdo_dblib @@ -1933,6 +1934,7 @@ Extensions: --with-esoob=DIR Include Easysoft OOB support /usr/local/easysoft/oob/client --with-unixODBC=DIR Include unixODBC support /usr/local --with-dbmaker=DIR Include DBMaker support + --enable-opcache Enable Zend OPcache support --enable-pcntl Enable pcntl support (CLI/CGI only) --disable-pdo Disable PHP Data Objects support --with-pdo-dblib=DIR PDO: DBLIB-DB support. DIR is the FreeTDS home directory @@ -3684,7 +3686,7 @@ ac_config_headers="$ac_config_headers main/php_config.h" PHP_MAJOR_VERSION=5 PHP_MINOR_VERSION=5 PHP_RELEASE_VERSION=0 -PHP_EXTRA_VERSION="alpha6" +PHP_EXTRA_VERSION="beta1" PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION" PHP_VERSION_ID=`expr $PHP_MAJOR_VERSION \* 10000 + $PHP_MINOR_VERSION \* 100 + $PHP_RELEASE_VERSION` @@ -10801,6 +10803,66 @@ ext_output=$PHP_CLI +for ac_func in setproctitle +do : + ac_fn_c_check_func "$LINENO" "setproctitle" "ac_cv_func_setproctitle" +if test "x$ac_cv_func_setproctitle" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SETPROCTITLE 1 +_ACEOF + +fi +done + + +for ac_header in sys/pstat.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "sys/pstat.h" "ac_cv_header_sys_pstat_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_pstat_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_PSTAT_H 1 +_ACEOF + +fi + +done + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PS_STRINGS" >&5 +$as_echo_n "checking for PS_STRINGS... " >&6; } +if ${cli_cv_var_PS_STRINGS+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <machine/vmparam.h> +#include <sys/exec.h> + +int +main () +{ +PS_STRINGS->ps_nargvstr = 1; +PS_STRINGS->ps_argvstr = "foo"; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + cli_cv_var_PS_STRINGS=yes +else + cli_cv_var_PS_STRINGS=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cli_cv_var_PS_STRINGS" >&5 +$as_echo "$cli_cv_var_PS_STRINGS" >&6; } +if test "$cli_cv_var_PS_STRINGS" = yes ; then + +$as_echo "#define HAVE_PS_STRINGS /**/" >>confdefs.h + +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CLI build" >&5 $as_echo_n "checking for CLI build... " >&6; } if test "$PHP_CLI" != "no"; then @@ -10884,7 +10946,7 @@ if test "$PHP_CLI" != "no"; then old_IFS=$IFS - for ac_src in php_cli.c php_http_parser.c php_cli_server.c; do + for ac_src in php_cli.c php_http_parser.c php_cli_server.c ps_title.c php_cli_process_title.c; do IFS=. set $ac_src @@ -67126,6 +67188,817 @@ fi +php_enable_opcache=yes + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable Zend OPcache support" >&5 +$as_echo_n "checking whether to enable Zend OPcache support... " >&6; } +# Check whether --enable-opcache was given. +if test "${enable_opcache+set}" = set; then : + enableval=$enable_opcache; PHP_OPCACHE=$enableval +else + + PHP_OPCACHE=yes + test "$PHP_ENABLE_ALL" && PHP_OPCACHE=$PHP_ENABLE_ALL + +fi + + + +ext_output="yes, shared" +ext_shared=yes +case $PHP_OPCACHE in +shared,*) + PHP_OPCACHE=`echo "$PHP_OPCACHE"|$SED 's/^shared,//'` + ;; +shared) + PHP_OPCACHE=yes + ;; +no) + ext_output=no + ext_shared=no + ;; +*) + ext_output=yes + ext_shared=no + ;; +esac + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ext_output" >&5 +$as_echo "$ext_output" >&6; } + + + + +if test "$PHP_OPCACHE" != "no"; then + + ac_fn_c_check_func "$LINENO" "mprotect" "ac_cv_func_mprotect" +if test "x$ac_cv_func_mprotect" = xyes; then : + + +$as_echo "#define HAVE_MPROTECT 1" >>confdefs.h + + +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysvipc shared memory support" >&5 +$as_echo_n "checking for sysvipc shared memory support... " >&6; } + if test "$cross_compiling" = yes; then : + msg=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#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; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +$as_echo "#define HAVE_SHM_IPC 1" >>confdefs.h + + msg=yes +else + msg=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $msg" >&5 +$as_echo "$msg" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mmap() using MAP_ANON shared memory support" >&5 +$as_echo_n "checking for mmap() using MAP_ANON shared memory support... " >&6; } + if test "$cross_compiling" = yes; then : + msg=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#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; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +$as_echo "#define HAVE_SHM_MMAP_ANON 1" >>confdefs.h + + msg=yes +else + msg=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $msg" >&5 +$as_echo "$msg" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mmap() using /dev/zero shared memory support" >&5 +$as_echo_n "checking for mmap() using /dev/zero shared memory support... " >&6; } + if test "$cross_compiling" = yes; then : + msg=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#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; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +$as_echo "#define HAVE_SHM_MMAP_ZERO 1" >>confdefs.h + + msg=yes +else + msg=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $msg" >&5 +$as_echo "$msg" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mmap() using shm_open() shared memory support" >&5 +$as_echo_n "checking for mmap() using shm_open() shared memory support... " >&6; } + if test "$cross_compiling" = yes; then : + msg=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#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; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +$as_echo "#define HAVE_SHM_MMAP_POSIX 1" >>confdefs.h + + msg=yes +else + msg=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $msg" >&5 +$as_echo "$msg" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mmap() using regular file shared memory support" >&5 +$as_echo_n "checking for mmap() using regular file shared memory support... " >&6; } + if test "$cross_compiling" = yes; then : + msg=no +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#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; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +$as_echo "#define HAVE_SHM_MMAP_FILE 1" >>confdefs.h + + msg=yes +else + msg=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $msg" >&5 +$as_echo "$msg" >&6; } + + + ext_builddir=ext/opcache + ext_srcdir=$abs_srcdir/ext/opcache + + ac_extra= + + if test "shared" != "shared" && test "shared" != "yes" && test "" != "cli"; then + PHP_OPCACHE_SHARED=no + + + case ext/opcache in + "") ac_srcdir="$abs_srcdir/"; unset ac_bdir; ac_inc="-I. -I$abs_srcdir" ;; + /*) ac_srcdir=`echo "ext/opcache"|cut -c 2-`"/"; ac_bdir=$ac_srcdir; ac_inc="-I$ac_bdir -I$abs_srcdir/$ac_bdir" ;; + *) ac_srcdir="$abs_srcdir/ext/opcache/"; ac_bdir="ext/opcache/"; ac_inc="-I$ac_bdir -I$ac_srcdir" ;; + esac + + + + b_c_pre=$php_c_pre + b_cxx_pre=$php_cxx_pre + b_c_meta=$php_c_meta + b_cxx_meta=$php_cxx_meta + b_c_post=$php_c_post + b_cxx_post=$php_cxx_post + b_lo=$php_lo + + + old_IFS=$IFS + for ac_src in 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; do + + IFS=. + set $ac_src + ac_obj=$1 + IFS=$old_IFS + + PHP_GLOBAL_OBJS="$PHP_GLOBAL_OBJS $ac_bdir$ac_obj.lo" + + case $ac_src in + *.c) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.s) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.S) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.cpp|*.cc|*.cxx) ac_comp="$b_cxx_pre $ac_extra $ac_inc $b_cxx_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_cxx_post" ;; + esac + + cat >>Makefile.objects<<EOF +$ac_bdir$ac_obj.lo: $ac_srcdir$ac_src + $ac_comp +EOF + done + + + EXT_STATIC="$EXT_STATIC opcache" + if test "shared" != "nocli"; then + EXT_CLI_STATIC="$EXT_CLI_STATIC opcache" + fi + else + if test "shared" = "shared" || test "shared" = "yes"; then + PHP_OPCACHE_SHARED=yes + + case ext/opcache in + "") ac_srcdir="$abs_srcdir/"; unset ac_bdir; ac_inc="-I. -I$abs_srcdir" ;; + /*) ac_srcdir=`echo "ext/opcache"|cut -c 2-`"/"; ac_bdir=$ac_srcdir; ac_inc="-I$ac_bdir -I$abs_srcdir/$ac_bdir" ;; + *) ac_srcdir="$abs_srcdir/ext/opcache/"; ac_bdir="ext/opcache/"; ac_inc="-I$ac_bdir -I$ac_srcdir" ;; + esac + + + + b_c_pre=$shared_c_pre + b_cxx_pre=$shared_cxx_pre + b_c_meta=$shared_c_meta + b_cxx_meta=$shared_cxx_meta + b_c_post=$shared_c_post + b_cxx_post=$shared_cxx_post + b_lo=$shared_lo + + + old_IFS=$IFS + for ac_src in 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; do + + IFS=. + set $ac_src + ac_obj=$1 + IFS=$old_IFS + + shared_objects_opcache="$shared_objects_opcache $ac_bdir$ac_obj.lo" + + case $ac_src in + *.c) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.s) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.S) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.cpp|*.cc|*.cxx) ac_comp="$b_cxx_pre $ac_extra $ac_inc $b_cxx_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_cxx_post" ;; + esac + + cat >>Makefile.objects<<EOF +$ac_bdir$ac_obj.lo: $ac_srcdir$ac_src + $ac_comp +EOF + done + + case $host_alias in + *netware*) + + install_modules="install-modules" + + case $host_alias in + *aix*) + suffix=so + link_cmd='$(LIBTOOL) --mode=link $(CC) $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) -Wl,-G -o '$ext_builddir'/phpopcache.la -export-dynamic -avoid-version -prefer-pic -module -rpath $(phplibdir) $(EXTRA_LDFLAGS) $(shared_objects_opcache) $(PHPOPCACHE_SHARED_LIBADD) && mv -f '$ext_builddir'/.libs/phpopcache.so '$ext_builddir'/phpopcache.so' + ;; + *netware*) + suffix=nlm + link_cmd='$(LIBTOOL) --mode=link $(CC) $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) -o $@ -shared -export-dynamic -avoid-version -prefer-pic -module -rpath $(phplibdir) $(EXTRA_LDFLAGS) $(shared_objects_opcache) -L$(top_builddir)/netware -lphp5lib $(OPCACHE_SHARED_LIBADD)' + ;; + *) + suffix=la + link_cmd='$(LIBTOOL) --mode=link $(CC) $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) -o $@ -export-dynamic -avoid-version -prefer-pic -module -rpath $(phplibdir) $(EXTRA_LDFLAGS) $(shared_objects_opcache) $(PHPOPCACHE_SHARED_LIBADD)' + ;; + esac + + if test "xyes" = "xyes"; then + PHP_ZEND_EX="$PHP_ZEND_EX \$(phplibdir)/phpopcache.$suffix" + else + PHP_MODULES="$PHP_MODULES \$(phplibdir)/phpopcache.$suffix" + fi + + PHP_VAR_SUBST="$PHP_VAR_SUBST shared_objects_opcache" + + cat >>Makefile.objects<<EOF +\$(phplibdir)/phpopcache.$suffix: $ext_builddir/phpopcache.$suffix + \$(LIBTOOL) --mode=install cp $ext_builddir/phpopcache.$suffix \$(phplibdir) + +$ext_builddir/phpopcache.$suffix: \$(shared_objects_opcache) \$(PHPOPCACHE_SHARED_DEPENDENCIES) + $link_cmd + +EOF + + ;; + *) + + install_modules="install-modules" + + case $host_alias in + *aix*) + suffix=so + link_cmd='$(LIBTOOL) --mode=link $(CC) $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) -Wl,-G -o '$ext_builddir'/opcache.la -export-dynamic -avoid-version -prefer-pic -module -rpath $(phplibdir) $(EXTRA_LDFLAGS) $(shared_objects_opcache) $(OPCACHE_SHARED_LIBADD) && mv -f '$ext_builddir'/.libs/opcache.so '$ext_builddir'/opcache.so' + ;; + *netware*) + suffix=nlm + link_cmd='$(LIBTOOL) --mode=link $(CC) $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) -o $@ -shared -export-dynamic -avoid-version -prefer-pic -module -rpath $(phplibdir) $(EXTRA_LDFLAGS) $(shared_objects_opcache) -L$(top_builddir)/netware -lphp5lib $(ACHE_SHARED_LIBADD)' + ;; + *) + suffix=la + link_cmd='$(LIBTOOL) --mode=link $(CC) $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) -o $@ -export-dynamic -avoid-version -prefer-pic -module -rpath $(phplibdir) $(EXTRA_LDFLAGS) $(shared_objects_opcache) $(OPCACHE_SHARED_LIBADD)' + ;; + esac + + if test "xyes" = "xyes"; then + PHP_ZEND_EX="$PHP_ZEND_EX \$(phplibdir)/opcache.$suffix" + else + PHP_MODULES="$PHP_MODULES \$(phplibdir)/opcache.$suffix" + fi + + PHP_VAR_SUBST="$PHP_VAR_SUBST shared_objects_opcache" + + cat >>Makefile.objects<<EOF +\$(phplibdir)/opcache.$suffix: $ext_builddir/opcache.$suffix + \$(LIBTOOL) --mode=install cp $ext_builddir/opcache.$suffix \$(phplibdir) + +$ext_builddir/opcache.$suffix: \$(shared_objects_opcache) \$(OPCACHE_SHARED_DEPENDENCIES) + $link_cmd + +EOF + + ;; + esac + +cat >>confdefs.h <<_ACEOF +#define COMPILE_DL_OPCACHE 1 +_ACEOF + + fi + fi + + if test "shared" != "shared" && test "shared" != "yes" && test "" = "cli"; then + PHP_OPCACHE_SHARED=no + case "$PHP_SAPI" in + cgi|embed) + + + case ext/opcache in + "") ac_srcdir="$abs_srcdir/"; unset ac_bdir; ac_inc="-I. -I$abs_srcdir" ;; + /*) ac_srcdir=`echo "ext/opcache"|cut -c 2-`"/"; ac_bdir=$ac_srcdir; ac_inc="-I$ac_bdir -I$abs_srcdir/$ac_bdir" ;; + *) ac_srcdir="$abs_srcdir/ext/opcache/"; ac_bdir="ext/opcache/"; ac_inc="-I$ac_bdir -I$ac_srcdir" ;; + esac + + + + b_c_pre=$php_c_pre + b_cxx_pre=$php_cxx_pre + b_c_meta=$php_c_meta + b_cxx_meta=$php_cxx_meta + b_c_post=$php_c_post + b_cxx_post=$php_cxx_post + b_lo=$php_lo + + + old_IFS=$IFS + for ac_src in 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; do + + IFS=. + set $ac_src + ac_obj=$1 + IFS=$old_IFS + + PHP_GLOBAL_OBJS="$PHP_GLOBAL_OBJS $ac_bdir$ac_obj.lo" + + case $ac_src in + *.c) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.s) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.S) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.cpp|*.cc|*.cxx) ac_comp="$b_cxx_pre $ac_extra $ac_inc $b_cxx_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_cxx_post" ;; + esac + + cat >>Makefile.objects<<EOF +$ac_bdir$ac_obj.lo: $ac_srcdir$ac_src + $ac_comp +EOF + done + + + EXT_STATIC="$EXT_STATIC opcache" + ;; + *) + + + case ext/opcache in + "") ac_srcdir="$abs_srcdir/"; unset ac_bdir; ac_inc="-I. -I$abs_srcdir" ;; + /*) ac_srcdir=`echo "ext/opcache"|cut -c 2-`"/"; ac_bdir=$ac_srcdir; ac_inc="-I$ac_bdir -I$abs_srcdir/$ac_bdir" ;; + *) ac_srcdir="$abs_srcdir/ext/opcache/"; ac_bdir="ext/opcache/"; ac_inc="-I$ac_bdir -I$ac_srcdir" ;; + esac + + + + b_c_pre=$php_c_pre + b_cxx_pre=$php_cxx_pre + b_c_meta=$php_c_meta + b_cxx_meta=$php_cxx_meta + b_c_post=$php_c_post + b_cxx_post=$php_cxx_post + b_lo=$php_lo + + + old_IFS=$IFS + for ac_src in 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; do + + IFS=. + set $ac_src + ac_obj=$1 + IFS=$old_IFS + + PHP_GLOBAL_OBJS="$PHP_GLOBAL_OBJS $ac_bdir$ac_obj.lo" + + case $ac_src in + *.c) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.s) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.S) ac_comp="$b_c_pre $ac_extra $ac_inc $b_c_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_c_post" ;; + *.cpp|*.cc|*.cxx) ac_comp="$b_cxx_pre $ac_extra $ac_inc $b_cxx_meta -c $ac_srcdir$ac_src -o $ac_bdir$ac_obj.$b_lo $b_cxx_post" ;; + esac + + cat >>Makefile.objects<<EOF +$ac_bdir$ac_obj.lo: $ac_srcdir$ac_src + $ac_comp +EOF + done + + + ;; + esac + EXT_CLI_STATIC="$EXT_CLI_STATIC opcache" + fi + + + BUILD_DIR="$BUILD_DIR $ext_builddir" + + + + if test "$ext_builddir" = "."; then + PHP_PECL_EXTENSION=opcache + + PHP_VAR_SUBST="$PHP_VAR_SUBST PHP_PECL_EXTENSION" + + fi + + + + + $php_shtool mkdir -p $ext_builddir/Optimizer + + +fi + + + php_enable_pcntl=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable pcntl support" >&5 @@ -104780,7 +105653,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<EOF -#line 104783 "configure" +#line 105656 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -106692,7 +107565,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 106695 "configure"' > conftest.$ac_ext + echo '#line 107568 "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -108084,7 +108957,7 @@ else LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat > conftest.$ac_ext <<EOF -#line 108087 "configure" +#line 108960 "configure" #include "confdefs.h" int main() { ; return 0; } @@ -108242,11 +109115,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"configure:108245: $lt_compile\"" >&5) + (eval echo "\"configure:109118: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "configure:108249: \$? = $ac_status" >&5 + echo "configure:109122: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -108540,11 +109413,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"configure:108543: $lt_compile\"" >&5) + (eval echo "\"configure:109416: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "configure:108547: \$? = $ac_status" >&5 + echo "configure:109420: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -108644,11 +109517,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"configure:108647: $lt_compile\"" >&5) + (eval echo "\"configure:109520: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "configure:108651: \$? = $ac_status" >&5 + echo "configure:109524: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -109108,7 +109981,7 @@ _LT_EOF # Determine the default libpath from the value encoded in an empty executable. cat > conftest.$ac_ext <<EOF -#line 109111 "configure" +#line 109984 "configure" #include "confdefs.h" int main() { ; return 0; } @@ -109150,7 +110023,7 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi # Determine the default libpath from the value encoded in an empty executable. cat > conftest.$ac_ext <<EOF -#line 109153 "configure" +#line 110026 "configure" #include "confdefs.h" int main() { ; return 0; } @@ -110675,7 +111548,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<EOF -#line 110678 "configure" +#line 111551 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -110775,7 +111648,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<EOF -#line 110778 "configure" +#line 111651 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -111840,7 +112713,7 @@ case $host_os in # Determine the default libpath from the value encoded in an empty executable. cat > conftest.$ac_ext <<EOF -#line 111843 "configure" +#line 112716 "configure" #include "confdefs.h" int main() { ; return 0; } @@ -111883,7 +112756,7 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi # Determine the default libpath from the value encoded in an empty executable. cat > conftest.$ac_ext <<EOF -#line 111886 "configure" +#line 112759 "configure" #include "confdefs.h" int main() { ; return 0; } @@ -113135,11 +114008,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"configure:113138: $lt_compile\"" >&5) + (eval echo "\"configure:114011: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "configure:113142: \$? = $ac_status" >&5 + echo "configure:114015: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -113239,11 +114112,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"configure:113242: $lt_compile\"" >&5) + (eval echo "\"configure:114115: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "configure:113246: \$? = $ac_status" >&5 + echo "configure:114119: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized diff --git a/configure.in b/configure.in index f0cb985ad..cc49903f8 100644 --- a/configure.in +++ b/configure.in @@ -120,7 +120,7 @@ int zend_sprintf(char *buffer, const char *format, ...); PHP_MAJOR_VERSION=5 PHP_MINOR_VERSION=5 PHP_RELEASE_VERSION=0 -PHP_EXTRA_VERSION="alpha6" +PHP_EXTRA_VERSION="beta1" PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION" PHP_VERSION_ID=`expr [$]PHP_MAJOR_VERSION \* 10000 + [$]PHP_MINOR_VERSION \* 100 + [$]PHP_RELEASE_VERSION` 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; } diff --git a/generated_lists b/generated_lists index 4d149666f..862cc3232 100644 --- a/generated_lists +++ b/generated_lists @@ -1,2 +1,2 @@ makefile_am_files = Zend/Makefile.am TSRM/Makefile.am -config_m4_files = Zend/Zend.m4 TSRM/tsrm.m4 TSRM/threads.m4 Zend/acinclude.m4 ext/bcmath/config.m4 ext/bz2/config.m4 ext/calendar/config.m4 ext/ctype/config.m4 ext/curl/config.m4 ext/date/config0.m4 ext/dba/config.m4 ext/dom/config.m4 ext/enchant/config.m4 ext/ereg/config0.m4 ext/exif/config.m4 ext/fileinfo/config.m4 ext/filter/config.m4 ext/ftp/config.m4 ext/gd/config.m4 ext/gettext/config.m4 ext/gmp/config.m4 ext/hash/config.m4 ext/iconv/config.m4 ext/imap/config.m4 ext/interbase/config.m4 ext/intl/config.m4 ext/json/config.m4 ext/ldap/config.m4 ext/libxml/config0.m4 ext/mbstring/config.m4 ext/mcrypt/config.m4 ext/mssql/config.m4 ext/mysql/config.m4 ext/mysqli/config.m4 ext/mysqlnd/config9.m4 ext/oci8/config.m4 ext/odbc/config.m4 ext/openssl/config0.m4 ext/pcntl/config.m4 ext/pcre/config0.m4 ext/pdo/config.m4 ext/pdo_dblib/config.m4 ext/pdo_firebird/config.m4 ext/pdo_mysql/config.m4 ext/pdo_oci/config.m4 ext/pdo_odbc/config.m4 ext/pdo_pgsql/config.m4 ext/pdo_sqlite/config.m4 ext/pgsql/config.m4 ext/phar/config.m4 ext/posix/config.m4 ext/pspell/config.m4 ext/readline/config.m4 ext/recode/config9.m4 ext/recode/config.m4 ext/reflection/config.m4 ext/session/config.m4 ext/shmop/config.m4 ext/simplexml/config.m4 ext/snmp/config.m4 ext/soap/config.m4 ext/sockets/config.m4 ext/spl/config.m4 ext/sqlite3/config0.m4 ext/standard/config.m4 ext/sybase_ct/config.m4 ext/sysvmsg/config.m4 ext/sysvsem/config.m4 ext/sysvshm/config.m4 ext/tidy/config.m4 ext/tokenizer/config.m4 ext/wddx/config.m4 ext/xml/config.m4 ext/xmlreader/config.m4 ext/xmlrpc/config.m4 ext/xmlwriter/config.m4 ext/xsl/config.m4 ext/zip/config.m4 ext/zlib/config0.m4 sapi/aolserver/config.m4 sapi/apache2filter/config.m4 sapi/apache2handler/config.m4 sapi/apache/config.m4 sapi/apache_hooks/config.m4 sapi/caudium/config.m4 sapi/cli/config.m4 sapi/continuity/config.m4 sapi/embed/config.m4 sapi/fpm/config.m4 sapi/isapi/config.m4 sapi/litespeed/config.m4 sapi/milter/config.m4 sapi/nsapi/config.m4 sapi/phttpd/config.m4 sapi/pi3web/config.m4 sapi/roxen/config.m4 sapi/thttpd/config.m4 sapi/tux/config.m4 sapi/webjames/config.m4 +config_m4_files = Zend/Zend.m4 TSRM/tsrm.m4 TSRM/threads.m4 Zend/acinclude.m4 ext/bcmath/config.m4 ext/bz2/config.m4 ext/calendar/config.m4 ext/ctype/config.m4 ext/curl/config.m4 ext/date/config0.m4 ext/dba/config.m4 ext/dom/config.m4 ext/enchant/config.m4 ext/ereg/config0.m4 ext/exif/config.m4 ext/fileinfo/config.m4 ext/filter/config.m4 ext/ftp/config.m4 ext/gd/config.m4 ext/gettext/config.m4 ext/gmp/config.m4 ext/hash/config.m4 ext/iconv/config.m4 ext/imap/config.m4 ext/interbase/config.m4 ext/intl/config.m4 ext/json/config.m4 ext/ldap/config.m4 ext/libxml/config0.m4 ext/mbstring/config.m4 ext/mcrypt/config.m4 ext/mssql/config.m4 ext/mysql/config.m4 ext/mysqli/config.m4 ext/mysqlnd/config9.m4 ext/oci8/config.m4 ext/odbc/config.m4 ext/opcache/config.m4 ext/openssl/config0.m4 ext/pcntl/config.m4 ext/pcre/config0.m4 ext/pdo/config.m4 ext/pdo_dblib/config.m4 ext/pdo_firebird/config.m4 ext/pdo_mysql/config.m4 ext/pdo_oci/config.m4 ext/pdo_odbc/config.m4 ext/pdo_pgsql/config.m4 ext/pdo_sqlite/config.m4 ext/pgsql/config.m4 ext/phar/config.m4 ext/posix/config.m4 ext/pspell/config.m4 ext/readline/config.m4 ext/recode/config9.m4 ext/recode/config.m4 ext/reflection/config.m4 ext/session/config.m4 ext/shmop/config.m4 ext/simplexml/config.m4 ext/snmp/config.m4 ext/soap/config.m4 ext/sockets/config.m4 ext/spl/config.m4 ext/sqlite3/config0.m4 ext/standard/config.m4 ext/sybase_ct/config.m4 ext/sysvmsg/config.m4 ext/sysvsem/config.m4 ext/sysvshm/config.m4 ext/tidy/config.m4 ext/tokenizer/config.m4 ext/wddx/config.m4 ext/xml/config.m4 ext/xmlreader/config.m4 ext/xmlrpc/config.m4 ext/xmlwriter/config.m4 ext/xsl/config.m4 ext/zip/config.m4 ext/zlib/config0.m4 sapi/aolserver/config.m4 sapi/apache2filter/config.m4 sapi/apache2handler/config.m4 sapi/apache/config.m4 sapi/apache_hooks/config.m4 sapi/caudium/config.m4 sapi/cli/config.m4 sapi/continuity/config.m4 sapi/embed/config.m4 sapi/fpm/config.m4 sapi/isapi/config.m4 sapi/litespeed/config.m4 sapi/milter/config.m4 sapi/nsapi/config.m4 sapi/phttpd/config.m4 sapi/pi3web/config.m4 sapi/roxen/config.m4 sapi/thttpd/config.m4 sapi/tux/config.m4 sapi/webjames/config.m4 diff --git a/main/network.c b/main/network.c index 88e166311..5c0404b23 100644 --- a/main/network.c +++ b/main/network.c @@ -872,7 +872,7 @@ skip_bind: efree(local_address); } } - /* free error string recieved during previous iteration (if any) */ + /* free error string received during previous iteration (if any) */ if (error_string && *error_string) { efree(*error_string); *error_string = NULL; diff --git a/main/php_config.h.in b/main/php_config.h.in index 8cb230f2f..6e76a4abf 100644 --- a/main/php_config.h.in +++ b/main/php_config.h.in @@ -132,6 +132,9 @@ /* Whether to build odbc as dynamic module */ #undef COMPILE_DL_ODBC +/* Whether to build opcache as dynamic module */ +#undef COMPILE_DL_OPCACHE + /* Whether to build openssl as dynamic module */ #undef COMPILE_DL_OPENSSL @@ -1195,6 +1198,9 @@ /* Define to 1 if you have the <monetary.h> header file. */ #undef HAVE_MONETARY_H +/* Define if you have mprotect() function */ +#undef HAVE_MPROTECT + /* Define to 1 if you have the `mremap' function. */ #undef HAVE_MREMAP @@ -1435,6 +1441,9 @@ /* */ #undef HAVE_PSPELL +/* Define to 1 if the PS_STRINGS thing exists. */ +#undef HAVE_PS_STRINGS + /* do we have ptrace? */ #undef HAVE_PTRACE @@ -1543,6 +1552,21 @@ /* */ #undef HAVE_SHMOP +/* Define if you have SysV IPC SHM support */ +#undef HAVE_SHM_IPC + +/* Define if you have mmap(MAP_ANON) SHM support */ +#undef HAVE_SHM_MMAP_ANON + +/* Define if you have mmap() SHM support */ +#undef HAVE_SHM_MMAP_FILE + +/* Define if you have POSIX mmap() SHM support */ +#undef HAVE_SHM_MMAP_POSIX + +/* Define if you have mmap("/dev/zero") SHM support */ +#undef HAVE_SHM_MMAP_ZERO + /* Define to 1 if you have the `shutdown' function. */ #undef HAVE_SHUTDOWN @@ -1842,6 +1866,9 @@ /* Define to 1 if you have the <sys/poll.h> header file. */ #undef HAVE_SYS_POLL_H +/* Define to 1 if you have the <sys/pstat.h> header file. */ +#undef HAVE_SYS_PSTAT_H + /* Define to 1 if you have the <sys/resource.h> header file. */ #undef HAVE_SYS_RESOURCE_H diff --git a/main/php_ini.c b/main/php_ini.c index b15a3846d..cb2c7ea80 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -290,7 +290,7 @@ static void php_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_t is_special_section = 1; has_per_dir_config = 1; - /* make the path lowercase on Windows, for case insensitivty. Does nothign for other platforms */ + /* make the path lowercase on Windows, for case insensitivity. Does nothing for other platforms */ TRANSLATE_SLASHES_LOWER(key); /* HOST sections */ @@ -357,7 +357,24 @@ static void php_load_php_extension_cb(void *arg TSRMLS_DC) */ static void php_load_zend_extension_cb(void *arg TSRMLS_DC) { - zend_load_extension(*((char **) arg)); + char *filename = *((char **) arg); + int length = strlen(filename); + + if (IS_ABSOLUTE_PATH(filename, length)) { + zend_load_extension(filename); + } else { + char *libpath; + char *extension_dir = INI_STR("extension_dir"); + int extension_dir_len = strlen(extension_dir); + + if (IS_SLASH(extension_dir[extension_dir_len-1])) { + spprintf(&libpath, 0, "%s%s", extension_dir, filename); + } else { + spprintf(&libpath, 0, "%s%c%s", extension_dir, DEFAULT_SLASH, filename); + } + zend_load_extension(libpath); + efree(libpath); + } } /* }}} */ diff --git a/main/php_version.h b/main/php_version.h index 7a57019aa..61c507b32 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -3,6 +3,6 @@ #define PHP_MAJOR_VERSION 5 #define PHP_MINOR_VERSION 5 #define PHP_RELEASE_VERSION 0 -#define PHP_EXTRA_VERSION "alpha6" -#define PHP_VERSION "5.5.0alpha6" +#define PHP_EXTRA_VERSION "beta1" +#define PHP_VERSION "5.5.0beta1" #define PHP_VERSION_ID 50500 diff --git a/run-tests.php b/run-tests.php index 8b7fa7f8b..84907a705 100755 --- a/run-tests.php +++ b/run-tests.php @@ -240,6 +240,7 @@ $ini_overwrites = array( 'ignore_repeated_errors=0', 'precision=14', 'memory_limit=128M', + 'opcache.fast_shutdown=0', ); function write_information($show_html) diff --git a/sapi/cli/config.m4 b/sapi/cli/config.m4 index cdfa1f7da..9a1b98da4 100644 --- a/sapi/cli/config.m4 +++ b/sapi/cli/config.m4 @@ -6,6 +6,23 @@ PHP_ARG_ENABLE(cli,, [ --disable-cli Disable building CLI version of PHP (this forces --without-pear)], yes, no) +AC_CHECK_FUNCS(setproctitle) + +AC_CHECK_HEADERS([sys/pstat.h]) + +AC_CACHE_CHECK([for PS_STRINGS], [cli_cv_var_PS_STRINGS], +[AC_TRY_LINK( +[#include <machine/vmparam.h> +#include <sys/exec.h> +], +[PS_STRINGS->ps_nargvstr = 1; +PS_STRINGS->ps_argvstr = "foo";], +[cli_cv_var_PS_STRINGS=yes], +[cli_cv_var_PS_STRINGS=no])]) +if test "$cli_cv_var_PS_STRINGS" = yes ; then + AC_DEFINE([HAVE_PS_STRINGS], [], [Define to 1 if the PS_STRINGS thing exists.]) +fi + AC_MSG_CHECKING(for CLI build) if test "$PHP_CLI" != "no"; then PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/cli/Makefile.frag) @@ -14,7 +31,7 @@ if test "$PHP_CLI" != "no"; then SAPI_CLI_PATH=sapi/cli/php dnl Select SAPI - PHP_SELECT_SAPI(cli, program, php_cli.c php_http_parser.c php_cli_server.c,, '$(SAPI_CLI_PATH)') + PHP_SELECT_SAPI(cli, program, php_cli.c php_http_parser.c php_cli_server.c ps_title.c php_cli_process_title.c,, '$(SAPI_CLI_PATH)') case $host_alias in *aix*) diff --git a/sapi/cli/config.w32 b/sapi/cli/config.w32 index 4d0dad58e..adcbb2b49 100644 --- a/sapi/cli/config.w32 +++ b/sapi/cli/config.w32 @@ -6,7 +6,7 @@ ARG_ENABLE('crt-debug', 'Enable CRT memory dumps for debugging sent to STDERR', ARG_ENABLE('cli-win32', 'Build console-less CLI version of PHP', 'no'); if (PHP_CLI == "yes") { - SAPI('cli', 'php_cli.c php_http_parser.c php_cli_server.c', 'php.exe'); + SAPI('cli', 'php_cli.c php_http_parser.c php_cli_server.c php_cli_process_title.c ps_title.c', 'php.exe'); ADD_FLAG("LIBS_CLI", "ws2_32.lib"); if (PHP_CRT_DEBUG == "yes") { ADD_FLAG("CFLAGS_CLI", "/D PHP_WIN32_DEBUG_HEAP"); @@ -15,7 +15,7 @@ if (PHP_CLI == "yes") { } if (PHP_CLI_WIN32 == "yes") { - SAPI('cli_win32', 'cli_win32.c', 'php-win.exe'); + SAPI('cli_win32', 'cli_win32.c php_cli_process_title.c ps_title.c', 'php-win.exe'); ADD_FLAG("LDFLAGS_CLI_WIN32", "/stack:8388608"); } diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index c01f3705b..4b8bae7f7 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -86,6 +86,9 @@ #include "php_cli_server.h" #endif +#include "ps_title.h" +#include "php_cli_process_title.h" + #ifndef PHP_WIN32 # define php_select(m, r, w, e, t) select(m, r, w, e, t) #else @@ -478,6 +481,8 @@ ZEND_END_ARG_INFO() static const zend_function_entry additional_functions[] = { ZEND_FE(dl, arginfo_dl) + PHP_FE(cli_set_process_title, arginfo_cli_set_process_title) + PHP_FE(cli_get_process_title, arginfo_cli_get_process_title) {NULL, NULL, NULL} }; @@ -1200,6 +1205,7 @@ int main(int argc, char *argv[]) int argc = __argc; char **argv = __argv; #endif + int c; int exit_status = SUCCESS; int module_started = 0, sapi_started = 0; @@ -1211,6 +1217,12 @@ int main(int argc, char *argv[]) int ini_ignore = 0; sapi_module_struct *sapi_module = &cli_sapi_module; + /* + * Do not move this initialization. It needs to happen before argv is used + * in any way. + */ + argv = save_ps_args(argc, argv); + cli_sapi_module.additional_functions = additional_functions; #if defined(PHP_WIN32) && defined(_DEBUG) && defined(PHP_WIN32_DEBUG_HEAP) @@ -1299,6 +1311,7 @@ int main(int argc, char *argv[]) #ifndef PHP_CLI_WIN32_NO_CONSOLE case 'S': sapi_module = &cli_server_sapi_module; + cli_server_sapi_module.additional_functions = server_additional_functions; break; #endif case 'h': /* help & quit */ @@ -1385,6 +1398,11 @@ out: tsrm_shutdown(); #endif + /* + * Do not move this de-initialization. It needs to happen right before + * exiting. + */ + cleanup_ps_args(argv); exit(exit_status); } /* }}} */ diff --git a/sapi/cli/php_cli_process_title.c b/sapi/cli/php_cli_process_title.c new file mode 100644 index 000000000..c50c412b1 --- /dev/null +++ b/sapi/cli/php_cli_process_title.c @@ -0,0 +1,80 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-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. | + +----------------------------------------------------------------------+ + | Author: Keyur Govande (kgovande@gmail.com) | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_cli_process_title.h" +#include "ps_title.h" + +/* {{{ proto boolean cli_set_process_title(string arg) + Return a boolean to confirm if the process title was successfully changed or not */ +PHP_FUNCTION(cli_set_process_title) +{ + char *title = NULL; + int title_len; + int rc; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &title, &title_len) == FAILURE) { + return; + } + + rc = set_ps_title(title); + if (rc == PS_TITLE_SUCCESS) { + RETURN_TRUE; + } + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cli_set_process_title had an error: %s", ps_title_errno(rc)); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto string cli_get_process_title() + Return a string with the current process title. NULL if error. */ +PHP_FUNCTION(cli_get_process_title) +{ + int length = 0; + const char* title = NULL; + int rc; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + rc = get_ps_title(&length, &title); + if (rc != PS_TITLE_SUCCESS) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "cli_get_process_title had an error: %s", ps_title_errno(rc)); + RETURN_NULL(); + } + + RETURN_STRINGL(title, length, 1); +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/sapi/cli/php_cli_process_title.h b/sapi/cli/php_cli_process_title.h new file mode 100644 index 000000000..b4b0861a2 --- /dev/null +++ b/sapi/cli/php_cli_process_title.h @@ -0,0 +1,43 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-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. | + +----------------------------------------------------------------------+ + | Author: Keyur Govande (kgovande@gmail.com) | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_PS_TITLE_HEADER +#define PHP_PS_TITLE_HEADER + +ZEND_BEGIN_ARG_INFO(arginfo_cli_set_process_title, 0) + ZEND_ARG_INFO(0, title) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_cli_get_process_title, 0) +ZEND_END_ARG_INFO() + +PHP_FUNCTION(cli_set_process_title); +PHP_FUNCTION(cli_get_process_title); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index 1d2ceff69..a2b85d47b 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -103,6 +103,8 @@ #include "php_http_parser.h" #include "php_cli_server.h" +#include "php_cli_process_title.h" + #define OUTPUT_NOT_CHECKED -1 #define OUTPUT_IS_TTY 1 #define OUTPUT_NOT_TTY 0 @@ -429,6 +431,12 @@ zend_module_entry cli_server_module_entry = { }; /* }}} */ +const zend_function_entry server_additional_functions[] = { + PHP_FE(cli_set_process_title, arginfo_cli_set_process_title) + PHP_FE(cli_get_process_title, arginfo_cli_get_process_title) + {NULL, NULL, NULL} +}; + static int sapi_cli_server_startup(sapi_module_struct *sapi_module) /* {{{ */ { if (php_module_startup(sapi_module, &cli_server_module_entry, 1) == FAILURE) { diff --git a/sapi/cli/php_cli_server.h b/sapi/cli/php_cli_server.h index ed716f99f..9a29626c7 100644 --- a/sapi/cli/php_cli_server.h +++ b/sapi/cli/php_cli_server.h @@ -23,6 +23,7 @@ #include "SAPI.h" +extern const zend_function_entry server_additional_functions[]; extern sapi_module_struct cli_server_sapi_module; extern int do_cli_server(int argc, char **argv TSRMLS_DC); diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c new file mode 100644 index 000000000..a2e47f031 --- /dev/null +++ b/sapi/cli/ps_title.c @@ -0,0 +1,426 @@ +/* + * PostgreSQL is released under the PostgreSQL License, a liberal Open Source + * license, similar to the BSD or MIT licenses. + * PostgreSQL Database Management System (formerly known as Postgres, then as + * Postgres95) + * + * Portions Copyright (c) 1996-2013, The PostgreSQL Global Development Group + * + * Portions Copyright (c) 1994, The Regents of the University of California + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without a written + * agreement is hereby granted, provided that the above copyright notice + * and this paragraph and the following two paragraphs appear in all copies. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING + * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, + * EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN + * "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * The following code is adopted from the PostgreSQL's ps_status(.h/.c). + */ + +#include "ps_title.h" +#include <stdio.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <string.h> +#include <stdlib.h> + +#ifdef PHP_WIN32 +#include "config.w32.h" +#include <windows.h> +#include <process.h> +#else +#include "php_config.h" +extern char** environ; +#endif + +#ifdef HAVE_SYS_PSTAT_H +#include <sys/pstat.h> /* for HP-UX */ +#endif +#ifdef HAVE_PS_STRINGS +#include <machine/vmparam.h> /* for old BSD */ +#include <sys/exec.h> +#endif +#if defined(DARWIN) +#include <crt_externs.h> +#endif + +/* + * Ways of updating ps display: + * + * PS_USE_SETPROCTITLE + * use the function setproctitle(const char *, ...) + * (newer BSD systems) + * PS_USE_PSTAT + * use the pstat(PSTAT_SETCMD, ) + * (HPUX) + * PS_USE_PS_STRINGS + * assign PS_STRINGS->ps_argvstr = "string" + * (some BSD systems) + * PS_USE_CHANGE_ARGV + * assign argv[0] = "string" + * (some other BSD systems) + * PS_USE_CLOBBER_ARGV + * write over the argv and environment area + * (Linux and most SysV-like systems) + * PS_USE_WIN32 + * push the string out as the name of a Windows event + * PS_USE_NONE + * don't update ps display + * (This is the default, as it is safest.) + */ +#if defined(HAVE_SETPROCTITLE) +#define PS_USE_SETPROCTITLE +#elif defined(HAVE_SYS_PSTAT_H) && defined(PSTAT_SETCMD) +#define PS_USE_PSTAT +#elif defined(HAVE_PS_STRINGS) +#define PS_USE_PS_STRINGS +#elif defined(BSD) && !defined(DARWIN) +#define PS_USE_CHANGE_ARGV +#elif defined(__linux__) || defined(_AIX) || defined(__sgi) || (defined(sun) && !defined(BSD)) || defined(ultrix) || defined(__osf__) || defined(DARWIN) +#define PS_USE_CLOBBER_ARGV +#elif defined(PHP_WIN32) +#define PS_USE_WIN32 +#else +#define PS_USE_NONE +#endif + +/* Different systems want the buffer padded differently */ +#if defined(_AIX) || defined(__linux__) || defined(DARWIN) +#define PS_PADDING '\0' +#else +#define PS_PADDING ' ' +#endif + +#ifdef PS_USE_WIN32 +static char windows_error_details[64]; +static char ps_buffer[MAX_PATH]; +static const size_t ps_buffer_size = MAX_PATH; +#elif defined(PS_USE_CLOBBER_ARGV) +static char *ps_buffer; /* will point to argv area */ +static size_t ps_buffer_size; /* space determined at run time */ +#else +#define PS_BUFFER_SIZE 256 +static char ps_buffer[PS_BUFFER_SIZE]; +static const size_t ps_buffer_size = PS_BUFFER_SIZE; +#endif + +static size_t ps_buffer_cur_len; /* actual string length in ps_buffer */ + +/* save the original argv[] location here */ +static int save_argc; +static char** save_argv; + + +/* + * Call this method early, before any code has used the original argv passed in + * from main(). + * If needed, this code will make deep copies of argv and environ and return + * these to the caller for further use. The original argv is then 'clobbered' + * to store the process title. + */ +char** save_ps_args(int argc, char** argv) +{ + save_argc = argc; + save_argv = argv; + +#if defined(PS_USE_CLOBBER_ARGV) + /* + * If we're going to overwrite the argv area, count the available space. + * Also move the environment to make additional room. + */ + { + char* end_of_area = NULL; + int non_contiguous_area = 0; + char** new_environ; + int i; + + /* + * check for contiguous argv strings + */ + for (i = 0; (non_contiguous_area == 0) && (i < argc); i++) + { + if (i != 0 && end_of_area + 1 != argv[i]) + non_contiguous_area = 1; + end_of_area = argv[i] + strlen(argv[i]); + } + + /* + * check for contiguous environ strings following argv + */ + for (i = 0; (non_contiguous_area == 0) && (environ[i] != NULL); i++) + { + if (end_of_area + 1 != environ[i]) + non_contiguous_area = 1; + end_of_area = environ[i] + strlen(environ[i]); + } + + if (non_contiguous_area != 0) + goto clobber_error; + + ps_buffer = argv[0]; + ps_buffer_size = end_of_area - argv[0]; + + /* + * move the environment out of the way + */ + new_environ = (char **) malloc((i + 1) * sizeof(char *)); + if (!new_environ) + goto clobber_error; + for (i = 0; environ[i] != NULL; i++) + { + new_environ[i] = strdup(environ[i]); + if (!new_environ[i]) + goto clobber_error; + } + new_environ[i] = NULL; + environ = new_environ; + + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) + /* + * If we're going to change the original argv[] then make a copy for + * argument parsing purposes. + * + * (NB: do NOT think to remove the copying of argv[]! + * On some platforms, getopt() keeps pointers into the argv array, and + * will get horribly confused when it is re-called to analyze a subprocess' + * argument string if the argv storage has been clobbered meanwhile. + * Other platforms have other dependencies on argv[].) + */ + { + char** new_argv; + int i; + + new_argv = (char **) malloc((argc + 1) * sizeof(char *)); + if (!new_argv) + goto clobber_error; + for (i = 0; i < argc; i++) + { + new_argv[i] = strdup(argv[i]); + if (!new_argv[i]) + goto clobber_error; + } + new_argv[argc] = NULL; + +#if defined(DARWIN) + /* + * Darwin (and perhaps other NeXT-derived platforms?) has a static + * copy of the argv pointer, which we may fix like so: + */ + *_NSGetArgv() = new_argv; +#endif + + argv = new_argv; + + } +#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ + +#if defined(PS_USE_CLOBBER_ARGV) + { + /* make extra argv slots point at end_of_area (a NUL) */ + int i; + for (i = 1; i < save_argc; i++) + save_argv[i] = ps_buffer + ps_buffer_size; + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#ifdef PS_USE_CHANGE_ARGV + save_argv[0] = ps_buffer; /* ps_buffer here is a static const array of size PS_BUFFER_SIZE */ + save_argv[1] = NULL; +#endif /* PS_USE_CHANGE_ARGV */ + + return argv; + +#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) +clobber_error: + /* probably can't happen?! + * if we ever get here, argv still points to originally passed + * in argument + */ + save_argv = NULL; + save_argc = 0; + ps_buffer = NULL; + ps_buffer_size = 0; + return argv; +#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ +} + +/* + * Returns PS_TITLE_SUCCESS if the OS supports this functionality + * and the init function was called. + * Otherwise returns NOT_AVAILABLE or NOT_INITIALIZED + */ +int is_ps_title_available() +{ +#ifdef PS_USE_NONE + return PS_TITLE_NOT_AVAILABLE; /* disabled functionality */ +#endif + + if (!save_argv) + return PS_TITLE_NOT_INITIALIZED; + +#ifdef PS_USE_CLOBBER_ARGV + if (!ps_buffer) + return PS_TITLE_BUFFER_NOT_AVAILABLE; +#endif /* PS_USE_CLOBBER_ARGV */ + + return PS_TITLE_SUCCESS; +} + +/* + * Convert error codes into error strings + */ +const char* ps_title_errno(int rc) +{ + switch(rc) + { + case PS_TITLE_SUCCESS: + return "Success"; + + case PS_TITLE_NOT_AVAILABLE: + return "Not available on this OS"; + + case PS_TITLE_NOT_INITIALIZED: + return "Not initialized correctly"; + + case PS_TITLE_BUFFER_NOT_AVAILABLE: + return "Buffer not contiguous"; + +#ifdef PS_USE_WIN32 + case PS_TITLE_WINDOWS_ERROR: + sprintf(windows_error_details, "Windows error code: %d", GetLastError()); + return windows_error_details; +#endif + } + + return "Unknown error code"; +} + +/* + * Set a new process title. + * Returns the appropriate error code if if there's an error + * (like the functionality is compile time disabled, or the + * save_ps_args() was not called. + * Else returns 0 on success. + */ +int set_ps_title(const char* title) +{ + int rc = is_ps_title_available(); + if (rc != PS_TITLE_SUCCESS) + return rc; + + strncpy(ps_buffer, title, ps_buffer_size); + ps_buffer[ps_buffer_size - 1] = '\0'; + ps_buffer_cur_len = strlen(ps_buffer); + +#ifdef PS_USE_SETPROCTITLE + setproctitle("%s", ps_buffer); +#endif + +#ifdef PS_USE_PSTAT + { + union pstun pst; + + pst.pst_command = ps_buffer; + pstat(PSTAT_SETCMD, pst, ps_buffer_cur_len, 0, 0); + } +#endif /* PS_USE_PSTAT */ + +#ifdef PS_USE_PS_STRINGS + PS_STRINGS->ps_nargvstr = 1; + PS_STRINGS->ps_argvstr = ps_buffer; +#endif /* PS_USE_PS_STRINGS */ + +#ifdef PS_USE_CLOBBER_ARGV + /* pad unused memory */ + if (ps_buffer_cur_len < ps_buffer_size) + { + memset(ps_buffer + ps_buffer_cur_len, PS_PADDING, + ps_buffer_size - ps_buffer_cur_len); + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#ifdef PS_USE_WIN32 + { + if (!SetConsoleTitle(ps_buffer)) + return PS_TITLE_WINDOWS_ERROR; + } +#endif /* PS_USE_WIN32 */ + + return PS_TITLE_SUCCESS; +} + +/* + * Returns the current ps_buffer value into string. On some platforms + * the string will not be null-terminated, so return the effective + * length into *displen. + * The return code indicates the error. + */ +int get_ps_title(int *displen, const char** string) +{ + int rc = is_ps_title_available(); + if (rc != PS_TITLE_SUCCESS) + return rc; + +#ifdef PS_USE_WIN32 + if (!(ps_buffer_cur_len = GetConsoleTitle(ps_buffer, ps_buffer_size))) + return PS_TITLE_WINDOWS_ERROR; +#endif + *displen = (int)ps_buffer_cur_len; + *string = ps_buffer; + return PS_TITLE_SUCCESS; +} + +/* + * Clean up the allocated argv and environ if applicable. Only call + * this right before exiting. + * This isn't needed per-se because the OS will clean-up anyway, but + * having and calling this will ensure Valgrind doesn't output 'false + * positives'. + */ +void cleanup_ps_args(char **argv) +{ +#ifndef PS_USE_NONE + if (save_argv) + { + save_argv = NULL; + save_argc = 0; + +#ifdef PS_USE_CLOBBER_ARGV + { + int i; + for (i = 0; environ[i] != NULL; i++) + free(environ[i]); + free(environ); + } +#endif /* PS_USE_CLOBBER_ARGV */ + +#if defined(PS_USE_CHANGE_ARGV) || defined(PS_USE_CLOBBER_ARGV) + { + int i; + for (i=0; argv[i] != NULL; i++) + free(argv[i]); + free(argv); + } +#endif /* PS_USE_CHANGE_ARGV or PS_USE_CLOBBER_ARGV */ + } +#endif /* PS_USE_NONE */ + + return; +} diff --git a/sapi/cli/ps_title.h b/sapi/cli/ps_title.h new file mode 100644 index 000000000..5623653e4 --- /dev/null +++ b/sapi/cli/ps_title.h @@ -0,0 +1,42 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-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: Keyur Govande <kgovande@gmail.com> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef PS_TITLE_HEADER +#define PS_TITLE_HEADER + +#define PS_TITLE_SUCCESS 0 +#define PS_TITLE_NOT_AVAILABLE 1 +#define PS_TITLE_NOT_INITIALIZED 2 +#define PS_TITLE_BUFFER_NOT_AVAILABLE 3 +#define PS_TITLE_WINDOWS_ERROR 4 + +extern char** save_ps_args(int argc, char** argv); + +extern int set_ps_title(const char* new_str); + +extern int get_ps_title(int* displen, const char** string); + +extern const char* ps_title_errno(int rc); + +extern int is_ps_title_available(); + +extern void cleanup_ps_args(char **argv); + +#endif // PS_TITLE_HEADER diff --git a/sapi/cli/tests/cli_process_title_unix.phpt b/sapi/cli/tests/cli_process_title_unix.phpt new file mode 100644 index 000000000..c2632704c --- /dev/null +++ b/sapi/cli/tests/cli_process_title_unix.phpt @@ -0,0 +1,49 @@ +--TEST-- +Check cli_process_title support on Unix +--SKIPIF-- +<?php +if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') + die("skip"); +?> +--FILE-- +<?php +echo "*** Testing setting the process title ***\n"; + +$set_title = $original_title = uniqid("title", true); +$pid = getmypid(); + +if (cli_set_process_title($original_title) === true) + echo "Successfully set title\n"; + +$ps_output = shell_exec("ps -p $pid -o command | tail -n 1"); + +if ($ps_output === null) +{ + echo "ps failed\n"; + die(); +} + +$loaded_title = trim($ps_output); +if (strpos(strtoupper(substr(PHP_OS, 0, 13)), "BSD") !== false) +{ + // Fix up title for BSD + $set_title = "php: $original_title (php)"; +} + +if ($loaded_title == $set_title) + echo "Successfully verified title using ps\n"; +else + echo "Actually loaded from ps: $loaded_title\n"; + +$read_title = cli_get_process_title(); +if ($read_title == $original_title) + echo "Successfully verified title using get\n"; +else + echo "Actually loaded from get: $read_title\n"; + +?> +--EXPECTF-- +*** Testing setting the process title *** +Successfully set title +Successfully verified title using ps +Successfully verified title using get diff --git a/sapi/cli/tests/cli_process_title_windows.phpt b/sapi/cli/tests/cli_process_title_windows.phpt new file mode 100644 index 000000000..309c09c0e --- /dev/null +++ b/sapi/cli/tests/cli_process_title_windows.phpt @@ -0,0 +1,82 @@ +--TEST-- +Check cli_process_title support in Windows +--SKIPIF-- +<?php +if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') + die("skip"); +?> +--FILE-- +<?php + +// On Windows 8 and Server 2012, this test does not work the same way. When the PowerShell +// command "get-process" is executed using shell_exec, it overwrites the ConsoleTitle with +// "Windows PowerShell" and this title ONLY clears away when the php.exe process exits +// i.e. the test finishes. +// On older versions like Windows 7 though, running the command appends +// "Windows PowerShell" to the ConsoleTitle temporarily and the title reverts +// back to the original once shell_exec is done. +// Hence on Windows 8, we don't verify that the title is actually set by +// cli_set_process_title(). We're only making the API calls to ensure there are +// no warnings/errors. + +$is_windows8 = false; +$ps_output = shell_exec("PowerShell \"(Get-Host).UI.RawUI.WindowTitle\""); +if ($ps_output === null) +{ + echo "Get-Host failed\n"; + die(); +} + +$ps_output = trim($ps_output); +if (($ps_output == "Windows PowerShell") || ($ps_output == "Administrator: Windows PowerShell")) + $is_windows8 = true; + +echo "*** Testing setting the process title ***\n"; + +$original_title = uniqid("title", true); +$pid = getmypid(); + +if (cli_set_process_title($original_title) === true) + echo "Successfully set title\n"; + +if ($is_windows8) +{ + $loaded_title = $original_title; +} +else +{ + $loaded_title = shell_exec("PowerShell \"get-process cmd*,powershell* | Select-Object mainWindowTitle | ft -hide\""); + + if ($loaded_title === null) + { + echo "Reading title using get-process failed\n"; + die(); + } + + // Kind of convoluted. So when the test is run on Windows 7 or older, the console where + // the run-tests.php is executed forks a php.exe, which forks a cmd.exe, which then forks + // a final php.exe to run the actual test. But the console title is set for the original console. + // I couldn't figure out a good way to navigate this, so we're "grep'ing" all possible + // console windows for our very unique title. It should occur exactly once in the grep + // output. + if (substr_count($loaded_title, $original_title, 0) == 1) + $loaded_title = $original_title; +} + +if ($loaded_title == $original_title) + echo "Successfully verified title using get-process\n"; +else + echo "Actually loaded from get-process: $loaded_title\n"; + +$read_title = cli_get_process_title(); +if (substr_count($read_title, $original_title, 0) == 1) + echo "Successfully verified title using get\n"; +else + echo "Actually loaded from get: $read_title\n"; + +?> +--EXPECTF-- +*** Testing setting the process title *** +Successfully set title +Successfully verified title using get-process +Successfully verified title using get
\ No newline at end of file diff --git a/tests/output/ob_017.phpt b/tests/output/ob_017.phpt index 517fafe99..1dc5fc59b 100644 --- a/tests/output/ob_017.phpt +++ b/tests/output/ob_017.phpt @@ -1,5 +1,7 @@ --TEST-- output buffering - stati +--INI-- +opcache.optimization_level=0 --FILE-- <?php $stati = array(); diff --git a/tests/output/ob_start_basic_004.phpt b/tests/output/ob_start_basic_004.phpt index 16f09e8a9..710df7a8a 100644 --- a/tests/output/ob_start_basic_004.phpt +++ b/tests/output/ob_start_basic_004.phpt @@ -1,5 +1,7 @@ --TEST-- ob_start() chunk_size: confirm buffer is flushed after any output call that causes its length to equal or exceed chunk_size. +--INI-- +opcache.optimization_level=0 --FILE-- <?php /* diff --git a/win32/build/mkdist.php b/win32/build/mkdist.php index 5ed9bdc81..947af9fbe 100644 --- a/win32/build/mkdist.php +++ b/win32/build/mkdist.php @@ -443,7 +443,7 @@ function make_phar_dot_phar($dist_dir) $phar->setStub(implode('', $stub)); echo "Creating phar.phar.bat\n"; - file_put_contents($dist_dir . '/phar.phar.bat', "%~dp0php.exe %~dp0pharcommand.phar %*\r\n"); + file_put_contents($dist_dir . '/phar.phar.bat', "\"%~dp0php.exe\" \"%~dp0pharcommand.phar\" %*\r\n"); } if (!is_dir($test_dir)) { diff --git a/win32/globals.c b/win32/globals.c index 1bbb3b448..b381cc123 100644 --- a/win32/globals.c +++ b/win32/globals.c @@ -65,8 +65,6 @@ PHP_RSHUTDOWN_FUNCTION(win32_core_globals) ; closelog(); - wg->starttime.tv_sec = 0; - wg->lasttime = 0; return SUCCESS; } diff --git a/win32/php_win32_globals.h b/win32/php_win32_globals.h index 1686e5df6..b2b07f68e 100644 --- a/win32/php_win32_globals.h +++ b/win32/php_win32_globals.h @@ -38,10 +38,6 @@ struct _php_win32_core_globals { char *log_header; HANDLE log_source; - /* time */ - struct timeval starttime; - __int64 lasttime, freq; - HKEY registry_key; HANDLE registry_event; HashTable *registry_directories; diff --git a/win32/time.c b/win32/time.c index 391a8a81e..77e4504cd 100644 --- a/win32/time.c +++ b/win32/time.c @@ -12,13 +12,6 @@ /* $Id$ */ - /** - * - * 04-Feb-2001 - * - Added patch by "Vanhanen, Reijo" <Reijo.Vanhanen@helsoft.fi> - * Improves accuracy of msec - */ - /* Include stuff ************************************************************ */ #include <config.w32.h> @@ -32,98 +25,56 @@ #include <errno.h> #include "php_win32_globals.h" -int getfilesystemtime(struct timeval *time_Info) +typedef VOID (WINAPI *MyGetSystemTimeAsFileTime)(LPFILETIME lpSystemTimeAsFileTime); + +static MyGetSystemTimeAsFileTime get_time_func(void) { - FILETIME ft; - __int64 ff; - ULARGE_INTEGER convFromft; - - GetSystemTimeAsFileTime(&ft); /* 100 ns blocks since 01-Jan-1641 */ - /* resolution seems to be 0.01 sec */ - /* - * Do not cast a pointer to a FILETIME structure to either a - * ULARGE_INTEGER* or __int64* value because it can cause alignment faults on 64-bit Windows. - * via http://technet.microsoft.com/en-us/library/ms724284(v=vs.85).aspx - */ - convFromft.HighPart = ft.dwHighDateTime; - convFromft.LowPart = ft.dwLowDateTime; - ff = convFromft.QuadPart; - - time_Info->tv_sec = (int)(ff/(__int64)10000000-(__int64)11644473600); - time_Info->tv_usec = (int)(ff % 10000000)/10; - return 0; + MyGetSystemTimeAsFileTime timefunc = NULL; + HMODULE hMod = LoadLibrary("kernel32.dll"); + + if (hMod) { + /* Max possible resolution <1us, win8/server2012 */ + timefunc = (MyGetSystemTimeAsFileTime)GetProcAddress(hMod, "GetSystemTimePreciseAsFileTime"); + + if(!timefunc) { + /* 100ns blocks since 01-Jan-1641 */ + timefunc = (MyGetSystemTimeAsFileTime)GetProcAddress(hMod, "GetSystemTimeAsFileTime"); + } + } + + return timefunc; } - +int getfilesystemtime(struct timeval *tv) +{ + FILETIME ft; + unsigned __int64 ff = 0; + MyGetSystemTimeAsFileTime timefunc; + + timefunc = get_time_func(); + if (timefunc) { + timefunc(&ft); + } else { + GetSystemTimeAsFileTime(&ft); + } + + ff |= ft.dwHighDateTime; + ff <<= 32; + ff |= ft.dwLowDateTime; + ff /= 10; /* convert to microseconds */ + ff -= 11644473600000000Ui64; /* convert to unix epoch */ + + tv->tv_sec = (long)(ff / 1000000UL); + tv->tv_usec = (long)(ff % 1000000UL); + + return 0; +} PHPAPI int gettimeofday(struct timeval *time_Info, struct timezone *timezone_Info) { - __int64 timer; - LARGE_INTEGER li; - BOOL b; - double dt; - TSRMLS_FETCH(); - /* Get the time, if they want it */ if (time_Info != NULL) { - if (PW32G(starttime).tv_sec == 0) { - b = QueryPerformanceFrequency(&li); - if (!b) { - PW32G(starttime).tv_sec = -1; - } - else { - PW32G(freq) = li.QuadPart; - b = QueryPerformanceCounter(&li); - if (!b) { - PW32G(starttime).tv_sec = -1; - } - else { - getfilesystemtime(&PW32G(starttime)); - timer = li.QuadPart; - dt = (double)timer/PW32G(freq); - PW32G(starttime).tv_usec -= (int)((dt-(int)dt)*1000000); - if (PW32G(starttime).tv_usec < 0) { - PW32G(starttime).tv_usec += 1000000; - --PW32G(starttime).tv_sec; - } - PW32G(starttime).tv_sec -= (int)dt; - } - } - } - if (PW32G(starttime).tv_sec > 0) { - b = QueryPerformanceCounter(&li); - if (!b) { - PW32G(starttime).tv_sec = -1; - } - else { - timer = li.QuadPart; - if (timer < PW32G(lasttime)) { - getfilesystemtime(time_Info); - dt = (double)timer/PW32G(freq); - PW32G(starttime) = *time_Info; - PW32G(starttime).tv_usec -= (int)((dt-(int)dt)*1000000); - if (PW32G(starttime).tv_usec < 0) { - PW32G(starttime).tv_usec += 1000000; - --PW32G(starttime).tv_sec; - } - PW32G(starttime).tv_sec -= (int)dt; - } - else { - PW32G(lasttime) = timer; - dt = (double)timer/PW32G(freq); - time_Info->tv_sec = PW32G(starttime).tv_sec + (int)dt; - time_Info->tv_usec = PW32G(starttime).tv_usec + (int)((dt-(int)dt)*1000000); - if (time_Info->tv_usec >= 1000000) { - time_Info->tv_usec -= 1000000; - ++time_Info->tv_sec; - } - } - } - } - if (PW32G(starttime).tv_sec < 0) { - getfilesystemtime(time_Info); - } - + getfilesystemtime(time_Info); } /* Get the timezone, if they want it */ if (timezone_Info != NULL) { |