diff --git a/.gitattributes b/.gitattributes index 9ed6e5f4b9b..79b218fcbac 100644 --- a/.gitattributes +++ b/.gitattributes @@ -23,6 +23,8 @@ sapi/continuity/capi.c ident Zend/RFCs/002.txt ident Zend/RFCs/003.txt ident NEWS merge=NEWS +UPGRADING merge=NEWS +UPGRADING.INTERNALS merge=NEWS /ext/bz2/tests/with_strings.phpt -crlf /ext/dom/tests/bug40836.phpt -crlf /ext/dom/tests/domelement.phpt -crlf diff --git a/.travis.yml b/.travis.yml index 2939e3e92a1..4d127fe0201 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ notifications: email: false env: - - REPORT_EXIT_STATUS=1 TEST_PHP_EXECUTABLE=./sapi/cli/php + - REPORT_EXIT_STATUS=1 before_script: # Compile PHP @@ -21,4 +21,4 @@ before_script: - . ./travis/ext/pdo_pgsql/setup.sh # Run PHPs run-tests.php -script: ./sapi/cli/php run-tests.php -g "FAIL,XFAIL,BORK,WARN,LEAK,SKIP" +script: ./sapi/cli/php run-tests.php -p `pwd`/sapi/cli/php -g "FAIL,XFAIL,BORK,WARN,LEAK,SKIP" --show-diff diff --git a/README.RELEASE_PROCESS b/README.RELEASE_PROCESS index 2512d3a8b52..58d3cd4e8e4 100644 --- a/README.RELEASE_PROCESS +++ b/README.RELEASE_PROCESS @@ -64,7 +64,8 @@ Do not use abbreviations for alpha and beta. ``git push --tags origin HEAD`` 8. run: ``./makedist 5.4.2RC2``, this will export the tree, create configure -and build three tarballs (gz,bz2 and xz). +and build three tarballs (gz,bz2 and xz). Make sure you use the same GNU Bison +version as snaps. Recent bison version are known to break ZTS. 9. Copy those three tarballs to www.php.net, in your homedir there should be a directory "downloads/". Copy them into there, so that the system can generate @@ -154,7 +155,15 @@ origin ``". 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 diff --git a/Zend/tests/bug55156.phpt b/Zend/tests/bug55156.phpt index 6c0ff768d1c..7d75ce3e944 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-- __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 00000000000..b0208c4ac38 --- /dev/null +++ b/Zend/tests/bug62343.phpt @@ -0,0 +1,13 @@ +--TEST-- +Bug #62343 (Show class_alias In get_declared_classes()) +--FILE-- + +--EXPECT-- +string(1) "b" +string(1) "a" diff --git a/Zend/tests/bug63976.phpt b/Zend/tests/bug63976.phpt new file mode 100644 index 00000000000..0ac09d9b3d1 --- /dev/null +++ b/Zend/tests/bug63976.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #63976 (Parent class incorrectly using child constant in class property) +--FILE-- +table); +?> +--EXPECT-- +string(3) "foo" diff --git a/Zend/tests/bug64239_1.phpt b/Zend/tests/bug64239_1.phpt new file mode 100644 index 00000000000..10d44c18170 --- /dev/null +++ b/Zend/tests/bug64239_1.phpt @@ -0,0 +1,28 @@ +--TEST-- +Bug #64239 (get_class_methods() changed behavior) +--FILE-- + Bmethod + [1] => t2method +) +Array +( + [0] => Bmethod + [1] => t2method +) diff --git a/Zend/tests/bug64239_2.phpt b/Zend/tests/bug64239_2.phpt new file mode 100644 index 00000000000..26cf8ee1afb --- /dev/null +++ b/Zend/tests/bug64239_2.phpt @@ -0,0 +1,58 @@ +--TEST-- +Bug #64239 (debug_backtrace() changed behavior) +--FILE-- +backtrace(); } +} + +class B { + use T2 { t2method as Bmethod; } +} + +class C extends A { +} + +trait T1 { + protected function backtrace() { + $b = new B(); + $b->Bmethod(); + } +} +trait T2 { + public function t2method() { + print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)); + } +} +$a = new A(); +$a->test(); + +$c = new C(); +$c->test(); +?> +--EXPECTF-- +Array +( + [0] => Array + ( + [file] => %sbug64239_2.php + [line] => %d + [function] => Bmethod + [class] => B + [type] => -> + ) + +) +Array +( + [0] => Array + ( + [file] => %sbug64239_2.php + [line] => %d + [function] => Bmethod + [class] => B + [type] => -> + ) + +) diff --git a/Zend/tests/bug64239_3.phpt b/Zend/tests/bug64239_3.phpt new file mode 100644 index 00000000000..15faeb9852d --- /dev/null +++ b/Zend/tests/bug64239_3.phpt @@ -0,0 +1,33 @@ +--TEST-- +Bug #64239 (debug_print_backtrace() changed behavior) +--FILE-- +Bmethod(); +$a->t2method(); + +$c = new C(); +$c->Bmethod(); +$c->t2method(); +?> +--EXPECTF-- +#0 A->Bmethod() called at [%sbug64239_3.php:%d] +#0 A->t2method() called at [%sbug64239_3.php:%d] +#0 C->Bmethod() called at [%sbug64239_3.php:%d] +#0 A->t2method() called at [%sbug64239_3.php:%d] diff --git a/Zend/tests/bug64239_4.phpt b/Zend/tests/bug64239_4.phpt new file mode 100644 index 00000000000..7ab761ef72e --- /dev/null +++ b/Zend/tests/bug64239_4.phpt @@ -0,0 +1,31 @@ +--TEST-- +Bug #64239 (debug_print_backtrace() changed behavior) +--FILE-- + +--EXPECTF-- +#0 A::Bmethod() called at [%sbug64239_4.php:%d] +#0 A::t2method() called at [%sbug64239_4.php:%d] +#0 C::Bmethod() called at [%sbug64239_4.php:%d] +#0 A::t2method() called at [%sbug64239_4.php:%d] diff --git a/Zend/tests/bug64354.phpt b/Zend/tests/bug64354.phpt new file mode 100644 index 00000000000..03a4b80b4bc --- /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-- +getMessage()); +} +?> +--EXPECTF-- +string(9) "serialize" diff --git a/Zend/tests/bug64417.phpt b/Zend/tests/bug64417.phpt new file mode 100644 index 00000000000..f3ef740b43e --- /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-- +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/bug64515.phpt b/Zend/tests/bug64515.phpt new file mode 100644 index 00000000000..5390a6cdcd9 --- /dev/null +++ b/Zend/tests/bug64515.phpt @@ -0,0 +1,12 @@ +--TEST-- +Bug #64515 (Memoryleak when using the same variablename 2times in function declaration) +--FILE-- + +--EXPECT-- +okey diff --git a/Zend/tests/generators/clone.phpt b/Zend/tests/generators/clone.phpt index 36811dfe6e7..22f44281218 100644 --- a/Zend/tests/generators/clone.phpt +++ b/Zend/tests/generators/clone.phpt @@ -1,32 +1,15 @@ --TEST-- -Generators can be cloned +Generators cannot be cloned --FILE-- current()); -$g1->next(); - -$g2 = clone $g1; -var_dump($g2->current()); -$g2->next(); - -var_dump($g2->current()); -var_dump($g1->current()); - -$g1->next(); -var_dump($g1->current()); +$gen = gen(); +clone $gen; ?> ---EXPECT-- -int(0) -int(1) -int(2) -int(1) -int(2) +--EXPECTF-- +Fatal error: Trying to clone an uncloneable object of class Generator in %s on line %d diff --git a/Zend/tests/generators/clone_after_object_call.phpt b/Zend/tests/generators/clone_after_object_call.phpt deleted file mode 100644 index 0a424268cc8..00000000000 --- a/Zend/tests/generators/clone_after_object_call.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -Cloning a generator after an object method was called ---FILE-- -b(); - yield; -} - -$g1 = gen(); -$g1->rewind(); -$g2 = clone $g1; - -echo "Done"; ---EXPECT-- -Done diff --git a/Zend/tests/generators/clone_with_foreach.phpt b/Zend/tests/generators/clone_with_foreach.phpt deleted file mode 100644 index b05ed073120..00000000000 --- a/Zend/tests/generators/clone_with_foreach.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -Cloning a generator with a foreach loop properly adds a ref for the loop var ---FILE-- -current()); - -$g2 = clone $g1; -var_dump($g2->current()); - -$g1->next(); -$g2->next(); -var_dump($g1->current()); -var_dump($g2->current()); - -unset($g1); -$g2->next(); -var_dump($g2->current()); - -?> ---EXPECT-- -int(1) -int(1) -int(2) -int(2) -int(3) diff --git a/Zend/tests/generators/clone_with_properties.phpt b/Zend/tests/generators/clone_with_properties.phpt deleted file mode 100644 index 900253c6822..00000000000 --- a/Zend/tests/generators/clone_with_properties.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -Tests cloning a generator with properties ---FILE-- -prop = 'val'; - -$g2 = clone $g1; -unset($g1); - -var_dump($g2->prop); - -?> ---EXPECT-- -string(3) "val" diff --git a/Zend/tests/generators/clone_with_stack.phpt b/Zend/tests/generators/clone_with_stack.phpt deleted file mode 100644 index 5a8e6d842ca..00000000000 --- a/Zend/tests/generators/clone_with_stack.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -A generator with an active stack can be cloned ---FILE-- -rewind(); -$g2 = clone $g1; -unset($g1); -$g2->send(10); - -?> ---EXPECT-- -string(10) "xxxxxxxxxx" diff --git a/Zend/tests/generators/clone_with_symbol_table.phpt b/Zend/tests/generators/clone_with_symbol_table.phpt deleted file mode 100644 index e1fefebd8fa..00000000000 --- a/Zend/tests/generators/clone_with_symbol_table.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -A generator using a symbol table can be cloned ---FILE-- - 'bar']); - - // interrupt - yield; - - var_dump($foo); -} - -$g1 = gen(); -$g1->rewind(); -$g2 = clone $g1; -unset($g1); -$g2->next(); - -?> ---EXPECT-- -string(3) "bar" diff --git a/Zend/tests/generators/clone_with_this.phpt b/Zend/tests/generators/clone_with_this.phpt deleted file mode 100644 index b242d851ebe..00000000000 --- a/Zend/tests/generators/clone_with_this.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -Cloning a generator method (with $this) ---FILE-- -foo = 'bar'; - yield; // interrupt - var_dump($this->foo); - } -} - -$g1 = (new Test)->gen(); -$g1->rewind(); // goto yield -$g2 = clone $g1; -unset($g1); -$g2->next(); - -?> ---EXPECT-- -string(3) "bar" diff --git a/Zend/tests/generators/errors/serialize_unserialize_error.phpt b/Zend/tests/generators/errors/serialize_unserialize_error.phpt index a8470b0a63a..aa2d4693f78 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 00000000000..5ae55a1be09 --- /dev/null +++ b/Zend/tests/generators/generator_with_nonscalar_keys.phpt @@ -0,0 +1,52 @@ +--TEST-- +Generators can return non-scalar keys +--FILE-- + [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/generators/nested_method_calls.phpt b/Zend/tests/generators/nested_method_calls.phpt index 98aee2e60b0..0d640b31c6b 100644 --- a/Zend/tests/generators/nested_method_calls.phpt +++ b/Zend/tests/generators/nested_method_calls.phpt @@ -26,10 +26,7 @@ $g2 = gen(new B); $g2->current(); $g1->next(); - -$g3 = clone $g2; -unset($g2); -$g3->next(); +$g2->next(); ?> --EXPECT-- diff --git a/Zend/tests/generators/yield_during_method_call.phpt b/Zend/tests/generators/yield_during_method_call.phpt index 5fbe84fff55..3a9914d58b7 100644 --- a/Zend/tests/generators/yield_during_method_call.phpt +++ b/Zend/tests/generators/yield_during_method_call.phpt @@ -22,14 +22,6 @@ $gen = gen(); $gen->rewind(); unset($gen); -// test cloning -$g1 = gen(); -$g1->rewind(); -$g2 = clone $g1; -unset($g1); -$g2->send('bar'); - ?> --EXPECT-- foo -bar diff --git a/Zend/tests/ns_026.phpt b/Zend/tests/ns_026.phpt index af2bf2ca556..45f88750ebb 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-- = 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 95c90ea753c..e867ca5dac6 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; @@ -3907,6 +3976,62 @@ ZEND_API void zend_restore_error_handling(zend_error_handling *saved TSRMLS_DC) } /* }}} */ +ZEND_API const char* zend_find_alias_name(zend_class_entry *ce, const char *name, zend_uint len) /* {{{ */ +{ + zend_trait_alias *alias, **alias_ptr; + + alias_ptr = ce->trait_aliases; + alias = *alias_ptr; + while (alias) { + if (alias->alias_len == len && + !strncasecmp(name, alias->alias, alias->alias_len)) { + return alias->alias; + } + alias_ptr++; + alias = *alias_ptr; + } + + return name; +} +/* }}} */ + +ZEND_API const char* zend_resolve_method_name(zend_class_entry *ce, zend_function *f) /* {{{ */ +{ + zend_function *func; + HashPosition iterator; + HashTable *function_table; + + if (f->common.type != ZEND_USER_FUNCTION || + *(f->op_array.refcount) < 2 || + !f->common.scope || + !f->common.scope->trait_aliases) { + return f->common.function_name; + } + + function_table = &ce->function_table; + zend_hash_internal_pointer_reset_ex(function_table, &iterator); + while (zend_hash_get_current_data_ex(function_table, (void **)&func, &iterator) == SUCCESS) { + if (func == f) { + char *name; + uint len; + ulong idx; + + if (zend_hash_get_current_key_ex(function_table, &name, &len, &idx, 0, &iterator) != HASH_KEY_IS_STRING) { + return f->common.function_name; + } + --len; + if (len == strlen(f->common.function_name) && + !strncasecmp(name, f->common.function_name, len)) { + return f->common.function_name; + } + return zend_find_alias_name(f->common.scope, name, len); + } + zend_hash_move_forward_ex(function_table, &iterator); + } + return f->common.function_name; +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_API.h b/Zend/zend_API.h index fb642c14755..26aa1e6b177 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); @@ -519,6 +521,9 @@ ZEND_API void zend_reset_all_cv(HashTable *symbol_table TSRMLS_DC); ZEND_API void zend_rebuild_symbol_table(TSRMLS_D); +ZEND_API const char* zend_find_alias_name(zend_class_entry *ce, const char *name, zend_uint len); +ZEND_API const char* zend_resolve_method_name(zend_class_entry *ce, zend_function *f); + #define add_method(arg, key, method) add_assoc_function((arg), (key), (method)) ZEND_API ZEND_FUNCTION(display_disabled_function); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 4d950a203b3..d61aba14bdd 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, zend_find_alias_name(mptr->common.scope, key, key_len - 1), 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; } @@ -2079,7 +2103,14 @@ ZEND_FUNCTION(debug_print_backtrace) lineno = 0; } - function_name = ptr->function_state.function->common.function_name; + function_name = (ptr->function_state.function->common.scope && + ptr->function_state.function->common.scope->trait_aliases) ? + zend_resolve_method_name( + ptr->object ? + Z_OBJCE_P(ptr->object) : + ptr->function_state.function->common.scope, + ptr->function_state.function) : + ptr->function_state.function->common.function_name; if (function_name) { if (ptr->object) { @@ -2260,7 +2291,14 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int filename = NULL; } - function_name = ptr->function_state.function->common.function_name; + function_name = (ptr->function_state.function->common.scope && + ptr->function_state.function->common.scope->trait_aliases) ? + zend_resolve_method_name( + ptr->object ? + Z_OBJCE_P(ptr->object) : + ptr->function_state.function->common.scope, + ptr->function_state.function) : + ptr->function_state.function->common.function_name; if (function_name) { add_assoc_string_ex(stack_frame, "function", sizeof("function"), (char*)function_name, 1); diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 0de92833377..5faefbd2241 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 c41cf475603..9d50641d219 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 e45419d3f0c..76807909183 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; } @@ -1801,7 +1809,7 @@ void zend_do_end_function_declaration(const znode *function_token TSRMLS_DC) /* zend_do_return(NULL, 0 TSRMLS_CC); pass_two(CG(active_op_array) TSRMLS_CC); - zend_release_labels(TSRMLS_C); + zend_release_labels(0 TSRMLS_CC); if (CG(active_class_entry)) { zend_check_magic_method_implementation(CG(active_class_entry), (zend_function*)CG(active_op_array), E_COMPILE_ERROR TSRMLS_CC); @@ -2383,13 +2391,14 @@ void zend_do_goto(const znode *label TSRMLS_DC) /* {{{ */ } /* }}} */ -void zend_release_labels(TSRMLS_D) /* {{{ */ +void zend_release_labels(int temporary TSRMLS_DC) /* {{{ */ { if (CG(context).labels) { zend_hash_destroy(CG(context).labels); FREE_HASHTABLE(CG(context).labels); + CG(context).labels = NULL; } - if (!zend_stack_is_empty(&CG(context_stack))) { + if (!temporary && !zend_stack_is_empty(&CG(context_stack))) { zend_compiler_context *ctx; zend_stack_top(&CG(context_stack), (void**)&ctx); @@ -3823,7 +3832,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 8042dd54eee..9c55b5ebe88 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -643,7 +643,7 @@ void zend_do_resolve_class_name(znode *result, znode *class_name, int is_static void zend_do_label(znode *label TSRMLS_DC); void zend_do_goto(const znode *label TSRMLS_DC); void zend_resolve_goto_label(zend_op_array *op_array, zend_op *opline, int pass2 TSRMLS_DC); -void zend_release_labels(TSRMLS_D); +void zend_release_labels(int temporary TSRMLS_DC); ZEND_API void function_add_ref(zend_function *function); @@ -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_execute.c b/Zend/zend_execute.c index a7674ca8ab4..b2d06238fab 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -898,13 +898,10 @@ static inline zval* zend_assign_to_variable(zval **variable_ptr_ptr, zval *value } else if (EXPECTED(!PZVAL_IS_REF(value))) { Z_ADDREF_P(value); *variable_ptr_ptr = value; - if (EXPECTED(variable_ptr != &EG(uninitialized_zval))) { - GC_REMOVE_ZVAL_FROM_BUFFER(variable_ptr); - zval_dtor(variable_ptr); - efree(variable_ptr); - } else { - Z_DELREF_P(variable_ptr); - } + ZEND_ASSERT(variable_ptr != &EG(uninitialized_zval)); + GC_REMOVE_ZVAL_FROM_BUFFER(variable_ptr); + zval_dtor(variable_ptr); + efree(variable_ptr); return value; } else { goto copy_value; diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index f099784a42a..8739e21c2b9 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1295,7 +1295,7 @@ void execute_new_code(TSRMLS_D) /* {{{ */ opline++; } - zend_release_labels(TSRMLS_C); + zend_release_labels(1 TSRMLS_CC); EG(return_value_ptr_ptr) = NULL; EG(active_op_array) = CG(active_op_array); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index c1dbee124f3..d189148f47f 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -196,175 +196,6 @@ static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* } /* }}} */ -static zend_object_value zend_generator_clone(zval *object TSRMLS_DC) /* {{{ */ -{ - zend_generator *orig = zend_object_store_get_object(object TSRMLS_CC); - zend_object_value clone_val = zend_generator_create(Z_OBJCE_P(object) TSRMLS_CC); - zend_generator *clone = zend_object_store_get_object_by_handle(clone_val.handle TSRMLS_CC); - - zend_objects_clone_members( - &clone->std, clone_val, &orig->std, Z_OBJ_HANDLE_P(object) TSRMLS_CC - ); - - clone->execute_data = orig->execute_data; - clone->largest_used_integer_key = orig->largest_used_integer_key; - clone->flags = orig->flags; - - if (orig->execute_data) { - /* Create a few shorter aliases to the old execution data */ - zend_execute_data *execute_data = orig->execute_data; - zend_op_array *op_array = execute_data->op_array; - HashTable *symbol_table = execute_data->symbol_table; - zend_execute_data *current_execute_data; - zend_op **opline_ptr; - HashTable *current_symbol_table; - zend_vm_stack current_stack; - zval *current_this; - void **stack_frame, **orig_stack_frame; - - /* Create new execution context. We have to back up and restore - * EG(current_execute_data), EG(opline_ptr), EG(active_symbol_table) - * and EG(This) here because the function modifies or uses them */ - current_execute_data = EG(current_execute_data); - EG(current_execute_data) = execute_data->prev_execute_data; - opline_ptr = EG(opline_ptr); - current_symbol_table = EG(active_symbol_table); - EG(active_symbol_table) = execute_data->symbol_table; - current_this = EG(This); - EG(This) = NULL; - current_stack = EG(argument_stack); - clone->execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC); - clone->stack = EG(argument_stack); - EG(argument_stack) = current_stack; - EG(This) = current_this; - EG(active_symbol_table) = current_symbol_table; - EG(current_execute_data) = current_execute_data; - EG(opline_ptr) = opline_ptr; - - /* copy */ - clone->execute_data->opline = execute_data->opline; - clone->execute_data->function_state = execute_data->function_state; - clone->execute_data->object = execute_data->object; - clone->execute_data->current_scope = execute_data->current_scope; - clone->execute_data->current_called_scope = execute_data->current_called_scope; - clone->execute_data->fast_ret = execute_data->fast_ret; - - if (!symbol_table) { - int i; - - /* Copy compiled variables */ - for (i = 0; i < op_array->last_var; i++) { - if (*EX_CV_NUM(execute_data, i)) { - *EX_CV_NUM(clone->execute_data, i) = (zval **) EX_CV_NUM(clone->execute_data, op_array->last_var + i); - **EX_CV_NUM(clone->execute_data, i) = *(zval **) EX_CV_NUM(execute_data, op_array->last_var + i); - Z_ADDREF_PP(*EX_CV_NUM(clone->execute_data, i)); - } - } - } else { - /* Copy symbol table */ - ALLOC_HASHTABLE(clone->execute_data->symbol_table); - zend_hash_init(clone->execute_data->symbol_table, zend_hash_num_elements(symbol_table), NULL, ZVAL_PTR_DTOR, 0); - zend_hash_copy(clone->execute_data->symbol_table, symbol_table, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); - - /* Update zval** pointers for compiled variables */ - { - int i; - for (i = 0; i < op_array->last_var; i++) { - if (zend_hash_quick_find(clone->execute_data->symbol_table, op_array->vars[i].name, op_array->vars[i].name_len + 1, op_array->vars[i].hash_value, (void **) EX_CV_NUM(clone->execute_data, i)) == FAILURE) { - *EX_CV_NUM(clone->execute_data, i) = NULL; - } - } - } - } - - /* Copy nested-calls stack */ - if (execute_data->call) { - clone->execute_data->call = clone->execute_data->call_slots + - (execute_data->call - execute_data->call_slots); - } else { - clone->execute_data->call = NULL; - } - memcpy(clone->execute_data->call_slots, execute_data->call_slots, ZEND_MM_ALIGNED_SIZE(sizeof(call_slot)) * op_array->nested_calls); - if (clone->execute_data->call >= clone->execute_data->call_slots) { - call_slot *call = clone->execute_data->call; - - while (call >= clone->execute_data->call_slots) { - if (call->object) { - Z_ADDREF_P(call->object); - } - call--; - } - } - - /* Copy the temporary variables */ - memcpy(EX_TMP_VAR_NUM(clone->execute_data, op_array->T-1), EX_TMP_VAR_NUM(execute_data, op_array->T-1), ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T); - - /* Copy arguments passed on stack */ - stack_frame = zend_vm_stack_frame_base(clone->execute_data); - orig_stack_frame = zend_vm_stack_frame_base(execute_data); - clone->stack->top = stack_frame + (orig->stack->top - orig_stack_frame); - if (clone->stack->top != stack_frame) { - memcpy(stack_frame, orig_stack_frame, ZEND_MM_ALIGNED_SIZE(sizeof(zval*)) * (orig->stack->top - orig_stack_frame)); - while (clone->stack->top != stack_frame) { - Z_ADDREF_PP((zval**)stack_frame); - stack_frame++; - } - } - - /* Add references to loop variables */ - { - zend_uint op_num = execute_data->opline - op_array->opcodes; - - int i; - for (i = 0; i < op_array->last_brk_cont; ++i) { - zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i; - - if (brk_cont->start < 0) { - continue; - } else if (brk_cont->start > op_num) { - break; - } else if (brk_cont->brk > op_num) { - zend_op *brk_opline = op_array->opcodes + brk_cont->brk; - - if (brk_opline->opcode == ZEND_SWITCH_FREE) { - temp_variable *var = EX_TMP_VAR(execute_data, brk_opline->op1.var); - - Z_ADDREF_P(var->var.ptr); - } - } - } - } - - /* Update the send_target to use the temporary variable with the same - * offset as the original generator, but in our temporary variable - * memory segment. */ - if (orig->send_target) { - size_t offset = (char *) orig->send_target - (char *)execute_data; - clone->send_target = EX_TMP_VAR(clone->execute_data, offset); - zval_copy_ctor(&clone->send_target->tmp_var); - } - - if (execute_data->current_this) { - clone->execute_data->current_this = execute_data->current_this; - Z_ADDREF_P(execute_data->current_this); - } - } - - /* The value and key are known not to be references, so simply add refs */ - if (orig->value) { - clone->value = orig->value; - Z_ADDREF_P(orig->value); - } - - if (orig->key) { - clone->key = orig->key; - Z_ADDREF_P(orig->key); - } - - return clone_val; -} -/* }}} */ - static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC) /* {{{ */ { zend_generator *generator; @@ -755,31 +586,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 (generator->key) { + ZVAL_ZVAL(key, generator->key, 1, 0); + } else { + ZVAL_NULL(key); } - - 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; - } - - /* 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 */ } /* }}} */ @@ -881,7 +698,7 @@ void zend_register_generator_ce(TSRMLS_D) /* {{{ */ memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); zend_generator_handlers.get_constructor = zend_generator_get_constructor; - zend_generator_handlers.clone_obj = zend_generator_clone; + zend_generator_handlers.clone_obj = NULL; } /* }}} */ diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 0609d707f53..bca47b330fd 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 88c3bfb421e..a0c147f3978 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_ini_parser.y b/Zend/zend_ini_parser.y index 2d428c3fe77..901babad60a 100644 --- a/Zend/zend_ini_parser.y +++ b/Zend/zend_ini_parser.y @@ -67,6 +67,9 @@ static void zend_ini_do_op(char type, zval *result, zval *op1, zval *op2) case '&': i_result = i_op1 & i_op2; break; + case '^': + i_result = i_op1 ^ i_op2; + break; case '~': i_result = ~i_op1; break; @@ -264,7 +267,7 @@ ZEND_API int zend_parse_ini_string(char *str, zend_bool unbuffered_errors, int s %token BOOL_FALSE %token END_OF_LINE %token '=' ':' ',' '.' '"' '\'' '^' '+' '-' '/' '*' '%' '$' '~' '<' '>' '?' '@' '{' '}' -%left '|' '&' +%left '|' '&' '^' %right '~' '!' %% @@ -348,6 +351,7 @@ expr: var_string_list { $$ = $1; } | expr '|' expr { zend_ini_do_op('|', &$$, &$1, &$3); } | expr '&' expr { zend_ini_do_op('&', &$$, &$1, &$3); } + | expr '^' expr { zend_ini_do_op('^', &$$, &$1, &$3); } | '~' expr { zend_ini_do_op('~', &$$, &$2, NULL); } | '!' expr { zend_ini_do_op('!', &$$, &$2, NULL); } | '(' expr ')' { $$ = $2; } diff --git a/Zend/zend_ini_scanner.c b/Zend/zend_ini_scanner.c index 35d9763bd96..87ba6643127 100644 --- a/Zend/zend_ini_scanner.c +++ b/Zend/zend_ini_scanner.c @@ -381,7 +381,7 @@ yyc_INITIAL: 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 128, 144, 144, 144, 144, + 144, 144, 144, 128, 144, 144, 128, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, @@ -419,6 +419,7 @@ yyc_INITIAL: case '&': case '(': case ')': + case '^': case '{': case '|': case '}': @@ -437,8 +438,7 @@ yyc_INITIAL: case '>': case '?': case '@': - case ']': - case '^': goto yy13; + case ']': goto yy13; case ';': goto yy14; case '=': goto yy16; case 'F': @@ -695,35 +695,33 @@ yy31: if (yybm[0+yych] & 64) { goto yy31; } - if (yych <= '&') { - if (yych <= '\r') { - if (yych <= '\t') { + if (yych <= '\'') { + if (yych <= ' ') { + if (yych <= '\n') { if (yych <= 0x08) goto yy25; - goto yy34; + if (yych <= '\t') goto yy34; } else { - if (yych <= '\n') goto yy33; - if (yych <= '\f') goto yy25; + if (yych != '\r') goto yy25; } } else { - if (yych <= '#') { - if (yych <= ' ') goto yy25; - if (yych >= '#') goto yy25; + if (yych <= '$') { + if (yych == '#') goto yy25; } else { - if (yych == '%') goto yy25; + if (yych != '&') goto yy25; } } } else { - if (yych <= '<') { - if (yych <= ')') { - if (yych <= '\'') goto yy25; + if (yych <= 'Z') { + if (yych <= ';') { + if (yych <= ')') goto yy33; + if (yych <= ':') goto yy25; } else { - if (yych != ';') goto yy25; + if (yych != '=') goto yy25; } } else { - if (yych <= '[') { - if (yych <= '=') goto yy33; - if (yych <= 'Z') goto yy25; - goto yy28; + if (yych <= '^') { + if (yych <= '[') goto yy28; + if (yych <= ']') goto yy25; } else { if (yych <= 'z') goto yy25; if (yych >= 0x7F) goto yy25; @@ -737,7 +735,7 @@ yy33: { /* TRUE value (when used outside option value/offset this causes parse error!) */ RETURN_TOKEN(BOOL_TRUE, "1", 1); } -#line 741 "Zend/zend_ini_scanner.c" +#line 739 "Zend/zend_ini_scanner.c" yy34: YYDEBUG(34, *YYCURSOR); ++YYCURSOR; @@ -769,36 +767,36 @@ yy39: YYFILL(1); yych = *YYCURSOR; YYDEBUG(40, *YYCURSOR); - if (yych <= '%') { - if (yych <= '\r') { - if (yych <= '\t') { + if (yych <= '&') { + if (yych <= 0x1F) { + if (yych <= '\n') { if (yych <= 0x08) goto yy25; - goto yy42; + if (yych <= '\t') goto yy42; } else { - if (yych <= '\n') goto yy41; - if (yych <= '\f') goto yy25; + if (yych != '\r') goto yy25; } } else { - if (yych <= '"') { - if (yych <= 0x1F) goto yy25; + if (yych <= '#') { if (yych <= ' ') goto yy39; + if (yych >= '#') goto yy25; } else { - if (yych != '$') goto yy25; + if (yych == '%') goto yy25; } } } else { - if (yych <= '<') { - if (yych <= ')') { - if (yych == '\'') goto yy25; + if (yych <= '=') { + if (yych <= ':') { + if (yych <= '\'') goto yy25; + if (yych >= '*') goto yy25; } else { - if (yych != ';') goto yy25; + if (yych == '<') goto yy25; } } else { - if (yych <= '[') { - if (yych <= '=') goto yy41; - if (yych <= 'Z') goto yy25; - goto yy28; + if (yych <= ']') { + if (yych == '[') goto yy28; + goto yy25; } else { + if (yych <= '^') goto yy41; if (yych <= 'z') goto yy25; if (yych >= 0x7F) goto yy25; } @@ -811,7 +809,7 @@ yy41: { /* FALSE value (when used outside option value/offset this causes parse error!)*/ RETURN_TOKEN(BOOL_FALSE, "", 0); } -#line 815 "Zend/zend_ini_scanner.c" +#line 813 "Zend/zend_ini_scanner.c" yy42: YYDEBUG(42, *YYCURSOR); ++YYCURSOR; @@ -824,7 +822,7 @@ yy42: yy44: YYDEBUG(44, *YYCURSOR); yych = *++YYCURSOR; - if (yych <= '&') { + if (yych <= '\'') { if (yych <= 0x1F) { if (yych <= '\n') { if (yych <= 0x08) goto yy26; @@ -841,22 +839,24 @@ yy44: goto yy26; } else { if (yych == '%') goto yy26; - goto yy41; + if (yych <= '&') goto yy41; + goto yy26; } } } else { - if (yych <= '=') { - if (yych <= ':') { - if (yych <= '\'') goto yy26; + if (yych <= 'N') { + if (yych <= ';') { if (yych <= ')') goto yy41; - goto yy26; - } else { - if (yych == '<') goto yy26; + if (yych <= ':') goto yy26; goto yy41; + } else { + if (yych == '=') goto yy41; + if (yych <= 'M') goto yy26; + goto yy47; } } else { if (yych <= 'm') { - if (yych == 'N') goto yy47; + if (yych == '^') goto yy41; goto yy26; } else { if (yych <= 'n') goto yy47; @@ -946,35 +946,34 @@ yy58: yych = *YYCURSOR; yy59: YYDEBUG(59, *YYCURSOR); - if (yych <= '&') { - if (yych <= '\r') { - if (yych <= '\t') { + if (yych <= '\'') { + if (yych <= ' ') { + if (yych <= '\n') { if (yych <= 0x08) goto yy58; + if (yych >= '\n') goto yy64; } else { - if (yych <= '\n') goto yy64; - if (yych <= '\f') goto yy58; - goto yy66; + if (yych == '\r') goto yy66; + goto yy58; } } else { - if (yych <= '#') { - if (yych <= ' ') goto yy58; - if (yych >= '#') goto yy58; + if (yych <= '$') { + if (yych == '#') goto yy58; } else { - if (yych == '%') goto yy58; + if (yych != '&') goto yy58; } } } else { - if (yych <= '<') { - if (yych <= ')') { - if (yych <= '\'') goto yy58; + if (yych <= 'Z') { + if (yych <= ';') { + if (yych <= ')') goto yy60; + if (yych <= ':') goto yy58; } else { - if (yych != ';') goto yy58; + if (yych != '=') goto yy58; } } else { - if (yych <= '[') { - if (yych <= '=') goto yy60; - if (yych <= 'Z') goto yy58; - goto yy62; + if (yych <= '^') { + if (yych <= '[') goto yy62; + if (yych <= ']') goto yy58; } else { if (yych <= 'z') goto yy58; if (yych >= 0x7F) goto yy58; @@ -1019,7 +1018,7 @@ yy65: SCNG(lineno)++; return END_OF_LINE; } -#line 1023 "Zend/zend_ini_scanner.c" +#line 1022 "Zend/zend_ini_scanner.c" yy66: YYDEBUG(66, *YYCURSOR); yych = *++YYCURSOR; @@ -1060,42 +1059,42 @@ yy69: YYFILL(2); yych = *YYCURSOR; YYDEBUG(70, *YYCURSOR); - if (yych <= '%') { - if (yych <= '\r') { - if (yych <= '\t') { + if (yych <= '&') { + if (yych <= 0x1F) { + if (yych <= '\n') { if (yych <= 0x08) goto yy25; - goto yy67; + if (yych <= '\t') goto yy67; } else { - if (yych <= '\n') goto yy71; - if (yych <= '\f') goto yy25; - goto yy72; + if (yych == '\r') goto yy72; + goto yy25; } } else { - if (yych <= '"') { - if (yych <= 0x1F) goto yy25; + if (yych <= '#') { if (yych <= ' ') goto yy69; - goto yy3; + if (yych <= '"') goto yy3; + goto yy58; } else { - if (yych <= '#') goto yy58; - if (yych <= '$') goto yy3; - goto yy25; + if (yych == '%') goto yy25; + goto yy3; } } } else { - if (yych <= '<') { - if (yych <= ')') { - if (yych == '\'') goto yy25; - goto yy3; - } else { - if (yych == ';') goto yy53; + if (yych <= '=') { + if (yych <= ':') { + if (yych <= '\'') goto yy25; + if (yych <= ')') goto yy3; goto yy25; + } else { + if (yych <= ';') goto yy53; + if (yych <= '<') goto yy25; + goto yy51; } } else { - if (yych <= '[') { - if (yych <= '=') goto yy51; - if (yych <= 'Z') goto yy25; - goto yy28; + if (yych <= ']') { + if (yych == '[') goto yy28; + goto yy25; } else { + if (yych <= '^') goto yy3; if (yych <= 'z') goto yy25; if (yych <= '~') goto yy3; goto yy25; @@ -1195,7 +1194,7 @@ yy76: zend_ini_escape_string(ini_lval, yytext, yyleng, '"' TSRMLS_CC); return TC_QUOTED_STRING; } -#line 1199 "Zend/zend_ini_scanner.c" +#line 1198 "Zend/zend_ini_scanner.c" yy77: YYDEBUG(77, *YYCURSOR); ++YYCURSOR; @@ -1209,7 +1208,7 @@ yy78: yy_pop_state(TSRMLS_C); return '"'; } -#line 1213 "Zend/zend_ini_scanner.c" +#line 1212 "Zend/zend_ini_scanner.c" yy79: YYDEBUG(79, *YYCURSOR); yych = *++YYCURSOR; @@ -1223,7 +1222,7 @@ yy79: yy_push_state(ST_VARNAME TSRMLS_CC); return TC_DOLLAR_CURLY; } -#line 1227 "Zend/zend_ini_scanner.c" +#line 1226 "Zend/zend_ini_scanner.c" yy82: YYDEBUG(82, *YYCURSOR); ++YYCURSOR; @@ -1327,7 +1326,7 @@ yy87: { /* Get rest as section/offset value */ RETURN_TOKEN(TC_STRING, yytext, yyleng); } -#line 1331 "Zend/zend_ini_scanner.c" +#line 1330 "Zend/zend_ini_scanner.c" yy88: YYDEBUG(88, *YYCURSOR); yyaccept = 0; @@ -1348,7 +1347,7 @@ yy90: { return 0; } -#line 1352 "Zend/zend_ini_scanner.c" +#line 1351 "Zend/zend_ini_scanner.c" yy91: YYDEBUG(91, *YYCURSOR); ++YYCURSOR; @@ -1360,7 +1359,7 @@ yy92: yy_push_state(ST_DOUBLE_QUOTES TSRMLS_CC); return '"'; } -#line 1364 "Zend/zend_ini_scanner.c" +#line 1363 "Zend/zend_ini_scanner.c" yy93: YYDEBUG(93, *YYCURSOR); yych = *++YYCURSOR; @@ -1426,7 +1425,7 @@ yy98: { /* Get number option value as string */ RETURN_TOKEN(TC_NUMBER, yytext, yyleng); } -#line 1430 "Zend/zend_ini_scanner.c" +#line 1429 "Zend/zend_ini_scanner.c" yy99: YYDEBUG(99, *YYCURSOR); yyaccept = 3; @@ -1456,7 +1455,7 @@ yy100: { /* Get constant option value */ RETURN_TOKEN(TC_CONSTANT, yytext, yyleng); } -#line 1460 "Zend/zend_ini_scanner.c" +#line 1459 "Zend/zend_ini_scanner.c" yy101: YYDEBUG(101, *YYCURSOR); yych = *++YYCURSOR; @@ -1472,7 +1471,7 @@ yy103: BEGIN(INITIAL); return ']'; } -#line 1476 "Zend/zend_ini_scanner.c" +#line 1475 "Zend/zend_ini_scanner.c" yy104: YYDEBUG(104, *YYCURSOR); yyaccept = 0; @@ -1771,7 +1770,7 @@ yy125: } RETURN_TOKEN(TC_RAW, yytext, yyleng); } -#line 1775 "Zend/zend_ini_scanner.c" +#line 1774 "Zend/zend_ini_scanner.c" yy129: YYDEBUG(129, *YYCURSOR); ++YYCURSOR; @@ -1782,7 +1781,7 @@ yy129: yy_push_state(ST_VARNAME TSRMLS_CC); return TC_DOLLAR_CURLY; } -#line 1786 "Zend/zend_ini_scanner.c" +#line 1785 "Zend/zend_ini_scanner.c" yy131: YYDEBUG(131, *YYCURSOR); yyaccept = 0; @@ -1892,7 +1891,7 @@ yyc_ST_RAW: BEGIN(INITIAL); return END_OF_LINE; } -#line 1896 "Zend/zend_ini_scanner.c" +#line 1895 "Zend/zend_ini_scanner.c" yy139: YYDEBUG(139, *YYCURSOR); ++YYCURSOR; @@ -1936,7 +1935,7 @@ end_raw_value_chars: } RETURN_TOKEN(TC_RAW, yytext, yyleng); } -#line 1940 "Zend/zend_ini_scanner.c" +#line 1939 "Zend/zend_ini_scanner.c" yy141: YYDEBUG(141, *YYCURSOR); yyaccept = 0; @@ -1967,7 +1966,7 @@ yy143: SCNG(lineno)++; return END_OF_LINE; } -#line 1971 "Zend/zend_ini_scanner.c" +#line 1970 "Zend/zend_ini_scanner.c" yy144: YYDEBUG(144, *YYCURSOR); yych = *++YYCURSOR; @@ -2001,7 +2000,7 @@ yy149: SCNG(lineno)++; return END_OF_LINE; } -#line 2005 "Zend/zend_ini_scanner.c" +#line 2004 "Zend/zend_ini_scanner.c" yy150: YYDEBUG(150, *YYCURSOR); yych = *++YYCURSOR; @@ -2035,7 +2034,7 @@ yy153: /* eat whitespace */ goto restart; } -#line 2039 "Zend/zend_ini_scanner.c" +#line 2038 "Zend/zend_ini_scanner.c" yy155: YYDEBUG(155, *YYCURSOR); ++YYCURSOR; @@ -2099,7 +2098,7 @@ yy159: { /* Raw value, only used when SCNG(scanner_mode) == ZEND_INI_SCANNER_RAW. */ RETURN_TOKEN(TC_RAW, yytext, yyleng); } -#line 2103 "Zend/zend_ini_scanner.c" +#line 2102 "Zend/zend_ini_scanner.c" yy160: YYDEBUG(160, *YYCURSOR); ++YYCURSOR; @@ -2109,7 +2108,7 @@ yy160: { return 0; } -#line 2113 "Zend/zend_ini_scanner.c" +#line 2112 "Zend/zend_ini_scanner.c" yy162: YYDEBUG(162, *YYCURSOR); ++YYCURSOR; @@ -2124,7 +2123,7 @@ yy163: SCNG(lineno)++; return ']'; } -#line 2128 "Zend/zend_ini_scanner.c" +#line 2127 "Zend/zend_ini_scanner.c" yy164: YYDEBUG(164, *YYCURSOR); ++YYCURSOR; @@ -2250,7 +2249,7 @@ yy173: { /* Get rest as section/offset value */ RETURN_TOKEN(TC_STRING, yytext, yyleng); } -#line 2254 "Zend/zend_ini_scanner.c" +#line 2253 "Zend/zend_ini_scanner.c" yy174: YYDEBUG(174, *YYCURSOR); yyaccept = 0; @@ -2273,7 +2272,7 @@ yy176: { return 0; } -#line 2277 "Zend/zend_ini_scanner.c" +#line 2276 "Zend/zend_ini_scanner.c" yy177: YYDEBUG(177, *YYCURSOR); ++YYCURSOR; @@ -2285,7 +2284,7 @@ yy178: yy_push_state(ST_DOUBLE_QUOTES TSRMLS_CC); return '"'; } -#line 2289 "Zend/zend_ini_scanner.c" +#line 2288 "Zend/zend_ini_scanner.c" yy179: YYDEBUG(179, *YYCURSOR); yych = *++YYCURSOR; @@ -2351,7 +2350,7 @@ yy184: { /* Get number option value as string */ RETURN_TOKEN(TC_NUMBER, yytext, yyleng); } -#line 2355 "Zend/zend_ini_scanner.c" +#line 2354 "Zend/zend_ini_scanner.c" yy185: YYDEBUG(185, *YYCURSOR); yyaccept = 3; @@ -2381,7 +2380,7 @@ yy186: { /* Get constant option value */ RETURN_TOKEN(TC_CONSTANT, yytext, yyleng); } -#line 2385 "Zend/zend_ini_scanner.c" +#line 2384 "Zend/zend_ini_scanner.c" yy187: YYDEBUG(187, *YYCURSOR); yych = *++YYCURSOR; @@ -2400,7 +2399,7 @@ yy189: SCNG(lineno)++; return ']'; } -#line 2404 "Zend/zend_ini_scanner.c" +#line 2403 "Zend/zend_ini_scanner.c" yy190: YYDEBUG(190, *YYCURSOR); ++YYCURSOR; @@ -2721,7 +2720,7 @@ yy215: } RETURN_TOKEN(TC_RAW, yytext, yyleng); } -#line 2725 "Zend/zend_ini_scanner.c" +#line 2724 "Zend/zend_ini_scanner.c" yy219: YYDEBUG(219, *YYCURSOR); ++YYCURSOR; @@ -2732,7 +2731,7 @@ yy219: yy_push_state(ST_VARNAME TSRMLS_CC); return TC_DOLLAR_CURLY; } -#line 2736 "Zend/zend_ini_scanner.c" +#line 2735 "Zend/zend_ini_scanner.c" yy221: YYDEBUG(221, *YYCURSOR); yyaccept = 0; @@ -2796,7 +2795,7 @@ yyc_ST_VALUE: 162, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, - 166, 166, 166, 162, 162, 162, 162, 166, + 166, 166, 166, 162, 162, 162, 160, 166, 162, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, @@ -2832,6 +2831,7 @@ yyc_ST_VALUE: case '&': case '(': case ')': + case '^': case '|': case '~': goto yy235; case '"': goto yy237; @@ -3024,35 +3024,34 @@ yy243: YYDEBUG(243, *YYCURSOR); yyaccept = 3; yych = *(YYMARKER = ++YYCURSOR); - if (yych <= '.') { - if (yych <= '\r') { - if (yych <= 0x08) { - if (yych >= 0x01) goto yy256; + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy244; + if (yych <= 0x08) goto yy256; } else { - if (yych <= '\n') goto yy244; - if (yych <= '\f') goto yy256; + if (yych != '\r') goto yy256; } } else { - if (yych <= '%') { - if (yych <= 0x1F) goto yy256; - if (yych >= '#') goto yy256; + if (yych <= ')') { + if (yych <= '"') goto yy244; + if (yych <= '%') goto yy256; } else { - if (yych <= ')') goto yy244; - if (yych <= '-') goto yy256; - goto yy288; + if (yych == '.') goto yy288; + goto yy256; } } } else { - if (yych <= '<') { - if (yych <= '9') { - if (yych <= '/') goto yy256; - goto yy290; + if (yych <= ']') { + if (yych <= ';') { + if (yych <= '9') goto yy290; + if (yych <= ':') goto yy256; } else { - if (yych != ';') goto yy256; + if (yych != '=') goto yy256; } } else { if (yych <= '|') { - if (yych <= '=') goto yy244; + if (yych <= '^') goto yy244; if (yych <= '{') goto yy256; } else { if (yych != '~') goto yy256; @@ -3066,7 +3065,7 @@ yy244: { /* Get number option value as string */ RETURN_TOKEN(TC_NUMBER, yytext, yyleng); } -#line 3070 "Zend/zend_ini_scanner.c" +#line 3069 "Zend/zend_ini_scanner.c" yy245: YYDEBUG(245, *YYCURSOR); yyaccept = 2; @@ -3083,7 +3082,7 @@ yy246: BEGIN(INITIAL); return END_OF_LINE; } -#line 3087 "Zend/zend_ini_scanner.c" +#line 3086 "Zend/zend_ini_scanner.c" yy248: YYDEBUG(248, *YYCURSOR); yyaccept = 4; @@ -3091,23 +3090,30 @@ yy248: if (yybm[0+yych] & 4) { goto yy257; } - if (yych <= ')') { - if (yych <= '\f') { - if (yych <= 0x00) goto yy249; - if (yych <= 0x08) goto yy256; - if (yych >= '\v') goto yy256; - } else { - if (yych <= 0x1F) { - if (yych >= 0x0E) goto yy256; + if (yych <= ':') { + if (yych <= '\r') { + if (yych <= 0x08) { + if (yych >= 0x01) goto yy256; + } else { + if (yych <= '\n') goto yy249; + if (yych <= '\f') goto yy256; + } + } else { + if (yych <= '"') { + if (yych <= 0x1F) goto yy256; } else { - if (yych <= '"') goto yy249; if (yych <= '%') goto yy256; + if (yych >= '*') goto yy256; } } } else { - if (yych <= '=') { - if (yych == ';') goto yy249; - if (yych <= '<') goto yy256; + if (yych <= '^') { + if (yych <= '<') { + if (yych >= '<') goto yy256; + } else { + if (yych <= '=') goto yy249; + if (yych <= ']') goto yy256; + } } else { if (yych <= '|') { if (yych <= '{') goto yy256; @@ -3123,7 +3129,7 @@ yy249: { /* Get constant option value */ RETURN_TOKEN(TC_CONSTANT, yytext, yyleng); } -#line 3127 "Zend/zend_ini_scanner.c" +#line 3133 "Zend/zend_ini_scanner.c" yy250: YYDEBUG(250, *YYCURSOR); yyaccept = 4; @@ -3151,18 +3157,20 @@ yy250: } } } else { - if (yych <= '`') { + if (yych <= '_') { if (yych <= 'A') { if (yych <= '=') goto yy249; if (yych <= '@') goto yy256; goto yy280; } else { if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; - goto yy256; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + goto yy257; } } else { if (yych <= '{') { + if (yych <= '`') goto yy256; if (yych <= 'a') goto yy280; if (yych <= 'z') goto yy257; goto yy256; @@ -3214,9 +3222,13 @@ yy251: if (yych == 'U') goto yy277; goto yy257; } else { - if (yych == '_') goto yy257; - if (yych <= '`') goto yy256; - goto yy257; + if (yych <= '^') { + if (yych <= ']') goto yy256; + goto yy249; + } else { + if (yych == '`') goto yy256; + goto yy257; + } } } else { if (yych <= 'z') { @@ -3275,9 +3287,13 @@ yy252: if (yych == 'N') goto yy265; goto yy257; } else { - if (yych == '_') goto yy257; - if (yych <= '`') goto yy256; - goto yy257; + if (yych <= '^') { + if (yych <= ']') goto yy256; + goto yy249; + } else { + if (yych == '`') goto yy256; + goto yy257; + } } } else { if (yych <= 'z') { @@ -3324,13 +3340,14 @@ yy253: } } else { if (yych <= '`') { - if (yych <= 'R') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'Q') goto yy257; - goto yy269; + if (yych == 'R') goto yy269; + goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -3374,13 +3391,14 @@ yy254: } } else { if (yych <= '`') { - if (yych <= 'E') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'D') goto yy257; - goto yy259; + if (yych == 'E') goto yy259; + goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -3418,7 +3436,7 @@ yy257: if (yybm[0+yych] & 4) { goto yy257; } - if (yych <= '%') { + if (yych <= ')') { if (yych <= '\r') { if (yych <= 0x08) { if (yych <= 0x00) goto yy249; @@ -3429,25 +3447,28 @@ yy257: goto yy249; } } else { - if (yych <= '"') { + if (yych <= '#') { if (yych <= 0x1F) goto yy255; - goto yy249; - } else { - if (yych == '$') goto yy260; + if (yych <= '"') goto yy249; goto yy255; + } else { + if (yych <= '$') goto yy260; + if (yych <= '%') goto yy255; + goto yy249; } } } else { - if (yych <= '=') { - if (yych <= ':') { - if (yych <= ')') goto yy249; - goto yy255; - } else { - if (yych == '<') goto yy255; + if (yych <= ']') { + if (yych <= ';') { + if (yych <= ':') goto yy255; goto yy249; + } else { + if (yych == '=') goto yy249; + goto yy255; } } else { if (yych <= '|') { + if (yych <= '^') goto yy249; if (yych <= '{') goto yy255; goto yy249; } else { @@ -3485,13 +3506,14 @@ yy259: } } else { if (yych <= '`') { - if (yych <= 'S') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'R') goto yy257; - goto yy265; + if (yych == 'S') goto yy265; + goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -3598,8 +3620,8 @@ yy265: if (yych != '=') goto yy256; } else { if (yych <= 'Z') goto yy257; - if (yych <= '^') goto yy256; - goto yy257; + if (yych <= ']') goto yy256; + if (yych >= '_') goto yy257; } } else { if (yych <= '{') { @@ -3619,7 +3641,7 @@ yy266: { /* TRUE value (when used outside option value/offset this causes parse error!) */ RETURN_TOKEN(BOOL_TRUE, "1", 1); } -#line 3623 "Zend/zend_ini_scanner.c" +#line 3645 "Zend/zend_ini_scanner.c" yy267: YYDEBUG(267, *YYCURSOR); ++YYCURSOR; @@ -3659,12 +3681,13 @@ yy269: } } else { if (yych <= '`') { - if (yych <= 'U') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'T') goto yy257; + if (yych != 'U') goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -3708,13 +3731,14 @@ yy270: } } else { if (yych <= '`') { - if (yych <= 'E') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'D') goto yy257; - goto yy265; + if (yych == 'E') goto yy265; + goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -3758,12 +3782,13 @@ yy271: } } else { if (yych <= '`') { - if (yych <= 'F') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'E') goto yy257; + if (yych != 'F') goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -3785,7 +3810,7 @@ yy272: if (yybm[0+yych] & 4) { goto yy257; } - if (yych <= '%') { + if (yych <= ')') { if (yych <= '\f') { if (yych <= 0x08) { if (yych >= 0x01) goto yy256; @@ -3794,22 +3819,25 @@ yy272: if (yych >= '\v') goto yy256; } } else { - if (yych <= 0x1F) { - if (yych >= 0x0E) goto yy256; + if (yych <= ' ') { + if (yych <= '\r') goto yy273; + if (yych <= 0x1F) goto yy256; + goto yy274; } else { - if (yych <= ' ') goto yy274; - if (yych >= '#') goto yy256; + if (yych <= '"') goto yy273; + if (yych <= '%') goto yy256; } } } else { - if (yych <= '=') { - if (yych <= ':') { - if (yych >= '*') goto yy256; + if (yych <= ']') { + if (yych <= ';') { + if (yych <= ':') goto yy256; } else { - if (yych == '<') goto yy256; + if (yych != '=') goto yy256; } } else { if (yych <= '|') { + if (yych <= '^') goto yy273; if (yych <= '{') goto yy256; } else { if (yych != '~') goto yy256; @@ -3823,7 +3851,7 @@ yy273: { /* FALSE value (when used outside option value/offset this causes parse error!)*/ RETURN_TOKEN(BOOL_FALSE, "", 0); } -#line 3827 "Zend/zend_ini_scanner.c" +#line 3855 "Zend/zend_ini_scanner.c" yy274: YYDEBUG(274, *YYCURSOR); ++YYCURSOR; @@ -3862,7 +3890,7 @@ yy276: } } } else { - if (yych <= '`') { + if (yych <= '_') { if (yych <= 'N') { if (yych <= '=') goto yy273; if (yych <= '@') goto yy256; @@ -3870,18 +3898,23 @@ yy276: goto yy279; } else { if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; - goto yy256; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy273; + goto yy257; } } else { - if (yych <= '{') { + if (yych <= 'z') { + if (yych <= '`') goto yy256; if (yych == 'n') goto yy279; - if (yych <= 'z') goto yy257; - goto yy256; + goto yy257; } else { - if (yych == '}') goto yy256; - if (yych <= '~') goto yy273; - goto yy256; + if (yych <= '|') { + if (yych <= '{') goto yy256; + goto yy273; + } else { + if (yych == '~') goto yy273; + goto yy256; + } } } } @@ -3914,12 +3947,13 @@ yy277: } } else { if (yych <= '`') { - if (yych <= 'L') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'K') goto yy257; + if (yych != 'L') goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -3963,13 +3997,14 @@ yy278: } } else { if (yych <= '`') { - if (yych <= 'L') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'K') goto yy257; - goto yy272; + if (yych == 'L') goto yy272; + goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -4013,13 +4048,14 @@ yy279: } } else { if (yych <= '`') { - if (yych <= 'E') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'D') goto yy257; - goto yy272; + if (yych == 'E') goto yy272; + goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -4063,12 +4099,13 @@ yy280: } } else { if (yych <= '`') { - if (yych <= 'L') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'K') goto yy257; + if (yych != 'L') goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -4112,12 +4149,13 @@ yy281: } } else { if (yych <= '`') { - if (yych <= 'S') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'R') goto yy257; + if (yych != 'S') goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -4161,13 +4199,14 @@ yy282: } } else { if (yych <= '`') { - if (yych <= 'E') { + if (yych <= 'Z') { if (yych <= '@') goto yy256; - if (yych <= 'D') goto yy257; - goto yy272; + if (yych == 'E') goto yy272; + goto yy257; } else { - if (yych <= 'Z') goto yy257; - if (yych == '_') goto yy257; + if (yych <= ']') goto yy256; + if (yych <= '^') goto yy249; + if (yych <= '_') goto yy257; goto yy256; } } else { @@ -4205,7 +4244,7 @@ yy286: SCNG(lineno)++; return END_OF_LINE; } -#line 4209 "Zend/zend_ini_scanner.c" +#line 4248 "Zend/zend_ini_scanner.c" yy287: YYDEBUG(287, *YYCURSOR); yych = *++YYCURSOR; @@ -4221,7 +4260,7 @@ yy288: if (yybm[0+yych] & 64) { goto yy288; } - if (yych <= '%') { + if (yych <= ')') { if (yych <= '\r') { if (yych <= 0x08) { if (yych <= 0x00) goto yy244; @@ -4232,25 +4271,28 @@ yy288: goto yy244; } } else { - if (yych <= '"') { + if (yych <= '#') { if (yych <= 0x1F) goto yy255; - goto yy244; - } else { - if (yych == '$') goto yy260; + if (yych <= '"') goto yy244; goto yy255; + } else { + if (yych <= '$') goto yy260; + if (yych <= '%') goto yy255; + goto yy244; } } } else { - if (yych <= '=') { - if (yych <= ':') { - if (yych <= ')') goto yy244; - goto yy255; - } else { - if (yych == '<') goto yy255; + if (yych <= ']') { + if (yych <= ';') { + if (yych <= ':') goto yy255; goto yy244; + } else { + if (yych == '=') goto yy244; + goto yy255; } } else { if (yych <= '|') { + if (yych <= '^') goto yy244; if (yych <= '{') goto yy255; goto yy244; } else { @@ -4266,7 +4308,57 @@ yy290: YYFILL(1); yych = *YYCURSOR; YYDEBUG(291, *YYCURSOR); - if (yych <= '-') { + if (yych <= '.') { + if (yych <= 0x1F) { + if (yych <= '\n') { + if (yych <= 0x00) goto yy244; + if (yych <= 0x08) goto yy255; + goto yy244; + } else { + if (yych == '\r') goto yy244; + goto yy255; + } + } else { + if (yych <= '$') { + if (yych <= '"') goto yy244; + if (yych <= '#') goto yy255; + goto yy260; + } else { + if (yych <= '%') goto yy255; + if (yych <= ')') goto yy244; + if (yych <= '-') goto yy255; + goto yy288; + } + } + } else { + if (yych <= '=') { + if (yych <= ':') { + if (yych <= '/') goto yy255; + if (yych <= '9') goto yy290; + goto yy255; + } else { + if (yych == '<') goto yy255; + goto yy244; + } + } else { + if (yych <= '{') { + if (yych == '^') goto yy244; + goto yy255; + } else { + if (yych == '}') goto yy255; + if (yych <= '~') goto yy244; + goto yy255; + } + } + } +yy292: + YYDEBUG(292, *YYCURSOR); + yyaccept = 3; + YYMARKER = ++YYCURSOR; + YYFILL(1); + yych = *YYCURSOR; + YYDEBUG(293, *YYCURSOR); + if (yych <= '/') { if (yych <= 0x1F) { if (yych <= '\n') { if (yych <= 0x00) goto yy244; @@ -4288,66 +4380,18 @@ yy290: } } } else { - if (yych <= '<') { - if (yych <= '9') { - if (yych <= '.') goto yy288; - if (yych <= '/') goto yy255; - goto yy290; + if (yych <= ']') { + if (yych <= ';') { + if (yych <= '9') goto yy292; + if (yych <= ':') goto yy255; + goto yy244; } else { - if (yych == ';') goto yy244; + if (yych == '=') goto yy244; goto yy255; } } else { if (yych <= '|') { - if (yych <= '=') goto yy244; - if (yych <= '{') goto yy255; - goto yy244; - } else { - if (yych == '~') goto yy244; - goto yy255; - } - } - } -yy292: - YYDEBUG(292, *YYCURSOR); - yyaccept = 3; - YYMARKER = ++YYCURSOR; - YYFILL(1); - yych = *YYCURSOR; - YYDEBUG(293, *YYCURSOR); - if (yych <= ')') { - if (yych <= '\r') { - if (yych <= 0x08) { - if (yych <= 0x00) goto yy244; - goto yy255; - } else { - if (yych <= '\n') goto yy244; - if (yych <= '\f') goto yy255; - goto yy244; - } - } else { - if (yych <= '#') { - if (yych <= 0x1F) goto yy255; - if (yych <= '"') goto yy244; - goto yy255; - } else { - if (yych <= '$') goto yy260; - if (yych <= '%') goto yy255; - goto yy244; - } - } - } else { - if (yych <= '<') { - if (yych <= '9') { - if (yych <= '/') goto yy255; - goto yy292; - } else { - if (yych == ';') goto yy244; - goto yy255; - } - } else { - if (yych <= '|') { - if (yych <= '=') goto yy244; + if (yych <= '^') goto yy244; if (yych <= '{') goto yy255; goto yy244; } else { @@ -4363,39 +4407,40 @@ yy294: YYFILL(1); yych = *YYCURSOR; YYDEBUG(295, *YYCURSOR); - if (yych <= ')') { - if (yych <= '\r') { - if (yych <= 0x08) { + if (yych <= '/') { + if (yych <= 0x1F) { + if (yych <= '\n') { if (yych <= 0x00) goto yy244; - goto yy255; - } else { - if (yych <= '\n') goto yy244; - if (yych <= '\f') goto yy255; + if (yych <= 0x08) goto yy255; goto yy244; + } else { + if (yych == '\r') goto yy244; + goto yy255; } } else { - if (yych <= '#') { - if (yych <= 0x1F) goto yy255; + if (yych <= '$') { if (yych <= '"') goto yy244; - goto yy255; + if (yych <= '#') goto yy255; + goto yy260; } else { - if (yych <= '$') goto yy260; if (yych <= '%') goto yy255; - goto yy244; + if (yych <= ')') goto yy244; + goto yy255; } } } else { - if (yych <= '<') { - if (yych <= '9') { - if (yych <= '/') goto yy255; - goto yy294; + if (yych <= ']') { + if (yych <= ';') { + if (yych <= '9') goto yy294; + if (yych <= ':') goto yy255; + goto yy244; } else { - if (yych == ';') goto yy244; + if (yych == '=') goto yy244; goto yy255; } } else { if (yych <= '|') { - if (yych <= '=') goto yy244; + if (yych <= '^') goto yy244; if (yych <= '{') goto yy255; goto yy244; } else { @@ -4426,7 +4471,7 @@ yy296: } RETURN_TOKEN(TC_RAW, yytext, yyleng); } -#line 4430 "Zend/zend_ini_scanner.c" +#line 4475 "Zend/zend_ini_scanner.c" yy300: YYDEBUG(300, *YYCURSOR); ++YYCURSOR; @@ -4437,7 +4482,7 @@ yy300: yy_push_state(ST_VARNAME TSRMLS_CC); return TC_DOLLAR_CURLY; } -#line 4441 "Zend/zend_ini_scanner.c" +#line 4486 "Zend/zend_ini_scanner.c" yy302: YYDEBUG(302, *YYCURSOR); ++YYCURSOR; @@ -4502,7 +4547,7 @@ yyc_ST_VARNAME: 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 128, 128, 128, 128, + 128, 128, 128, 0, 128, 128, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, @@ -4527,38 +4572,40 @@ yyc_ST_VARNAME: YYDEBUG(309, *YYCURSOR); YYFILL(2); yych = *YYCURSOR; - if (yych <= '\'') { - if (yych <= ' ') { - if (yych <= '\n') { - if (yych >= '\t') goto yy313; + if (yych <= ')') { + if (yych <= '"') { + if (yych <= '\f') { + if (yych <= 0x08) goto yy311; + if (yych <= '\n') goto yy313; } else { - if (yych == '\r') goto yy313; + if (yych <= '\r') goto yy313; + if (yych >= '!') goto yy313; } } else { - if (yych <= '$') { - if (yych != '#') goto yy313; + if (yych <= '%') { + if (yych == '$') goto yy313; } else { - if (yych == '&') goto yy313; + if (yych != '\'') goto yy313; } } } else { - if (yych <= 'Z') { - if (yych <= ';') { - if (yych <= ')') goto yy313; - if (yych >= ';') goto yy313; + if (yych <= '[') { + if (yych <= '<') { + if (yych == ';') goto yy313; } else { - if (yych == '=') goto yy313; + if (yych <= '=') goto yy313; + if (yych >= '[') goto yy313; } } else { - if (yych <= '|') { - if (yych <= '[') goto yy313; - if (yych >= '{') goto yy313; + if (yych <= 'z') { + if (yych == '^') goto yy313; } else { - if (yych <= '}') goto yy315; + if (yych == '}') goto yy315; if (yych <= '~') goto yy313; } } } +yy311: YYDEBUG(311, *YYCURSOR); ++YYCURSOR; yych = *YYCURSOR; @@ -4576,7 +4623,7 @@ yy312: RETURN_TOKEN(TC_VARNAME, yytext, yyleng); } -#line 4580 "Zend/zend_ini_scanner.c" +#line 4627 "Zend/zend_ini_scanner.c" yy313: YYDEBUG(313, *YYCURSOR); ++YYCURSOR; @@ -4586,7 +4633,7 @@ yy313: { return 0; } -#line 4590 "Zend/zend_ini_scanner.c" +#line 4637 "Zend/zend_ini_scanner.c" yy315: YYDEBUG(315, *YYCURSOR); ++YYCURSOR; @@ -4597,7 +4644,7 @@ yy315: yy_pop_state(TSRMLS_C); return '}'; } -#line 4601 "Zend/zend_ini_scanner.c" +#line 4648 "Zend/zend_ini_scanner.c" yy317: YYDEBUG(317, *YYCURSOR); ++YYCURSOR; diff --git a/Zend/zend_ini_scanner.l b/Zend/zend_ini_scanner.l index 5fb28d42595..92fb08f02d8 100644 --- a/Zend/zend_ini_scanner.l +++ b/Zend/zend_ini_scanner.l @@ -340,9 +340,9 @@ NEWLINE ("\r"|"\n"|"\r\n") TABS_AND_SPACES [ \t] WHITESPACE [ \t]+ CONSTANT [a-zA-Z_][a-zA-Z0-9_]* -LABEL [^=\n\r\t;|&$~(){}!"\[]+ -TOKENS [:,.\[\]"'()|^&+-/*=%$!~<>?@{}] -OPERATORS [&|~()!] +LABEL [^=\n\r\t;&|^$~(){}!"\[]+ +TOKENS [:,.\[\]"'()&|^+-/*=%$!~<>?@{}] +OPERATORS [&|^~()!] DOLLAR_CURLY "${" SECTION_RAW_CHARS [^\]\n\r] @@ -350,7 +350,7 @@ SINGLE_QUOTED_CHARS [^'] RAW_VALUE_CHARS [^\n\r;\000] LITERAL_DOLLAR ("$"([^{\000]|("\\"{ANY_CHAR}))) -VALUE_CHARS ([^$= \t\n\r;&|~()!"'\000]|{LITERAL_DOLLAR}) +VALUE_CHARS ([^$= \t\n\r;&|^~()!"'\000]|{LITERAL_DOLLAR}) SECTION_VALUE_CHARS ([^$\n\r;"'\]\\]|("\\"{ANY_CHAR})|{LITERAL_DOLLAR}) := yyleng = YYCURSOR - SCNG(yy_text); diff --git a/Zend/zend_interfaces.c b/Zend/zend_interfaces.c index 384b66da4b5..16751549b46 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 23547951ed7..ba4bc6ccb6d 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 b484102b20a..f74068a2718 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_language_parser.y b/Zend/zend_language_parser.y index ccbc9b174c9..6a9a24a87ea 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -41,17 +41,19 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); #define YYERROR_VERBOSE #define YYSTYPE znode -#ifdef ZTS -# define YYPARSE_PARAM tsrm_ls -# define YYLEX_PARAM tsrm_ls -#endif - %} %pure_parser %expect 3 +%code requires { +#ifdef ZTS +# define YYPARSE_PARAM tsrm_ls +# define YYLEX_PARAM tsrm_ls +#endif +} + %token END 0 "end of file" %left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE %token T_INCLUDE "include (T_INCLUDE)" diff --git a/Zend/zend_language_scanner.c b/Zend/zend_language_scanner.c index f2ba6bf9d4d..9a62a9c0459 100644 --- a/Zend/zend_language_scanner.c +++ b/Zend/zend_language_scanner.c @@ -597,7 +597,7 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type TSR CG(active_op_array) = original_active_op_array; if (compilation_successful) { pass_two(op_array TSRMLS_CC); - zend_release_labels(TSRMLS_C); + zend_release_labels(0 TSRMLS_CC); } else { efree(op_array); retval = NULL; @@ -772,7 +772,7 @@ zend_op_array *compile_string(zval *source_string, char *filename TSRMLS_DC) zend_do_return(NULL, 0 TSRMLS_CC); CG(active_op_array) = original_active_op_array; pass_two(op_array TSRMLS_CC); - zend_release_labels(TSRMLS_C); + zend_release_labels(0 TSRMLS_CC); retval = op_array; } } diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index d44ba20e1bb..23a2f472c06 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -595,7 +595,7 @@ ZEND_API zend_op_array *compile_file(zend_file_handle *file_handle, int type TSR CG(active_op_array) = original_active_op_array; if (compilation_successful) { pass_two(op_array TSRMLS_CC); - zend_release_labels(TSRMLS_C); + zend_release_labels(0 TSRMLS_CC); } else { efree(op_array); retval = NULL; @@ -770,7 +770,7 @@ zend_op_array *compile_string(zval *source_string, char *filename TSRMLS_DC) zend_do_return(NULL, 0 TSRMLS_CC); CG(active_op_array) = original_active_op_array; pass_two(op_array TSRMLS_CC); - zend_release_labels(TSRMLS_C); + zend_release_labels(0 TSRMLS_CC); retval = op_array; } } diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 3e68add3dc3..9cdf31fb343 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 0aaed2e465a..57cd54074af 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1971,13 +1971,6 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) LOAD_OPLINE(); if (fbc->type == ZEND_INTERNAL_FUNCTION) { - temp_variable *ret = &EX_T(opline->result.var); - - MAKE_STD_ZVAL(ret->var.ptr); - ZVAL_NULL(ret->var.ptr); - ret->var.ptr_ptr = &ret->var.ptr; - ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; - if (fbc->common.arg_info) { zend_uint i=0; zval **p = (zval**)EX(function_state).arguments; @@ -1989,15 +1982,24 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) } } - if (!zend_execute_internal) { - /* saves one function call if zend_execute_internal is not used */ - fbc->internal_function.handler(opline->extended_value, ret->var.ptr, (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? &ret->var.ptr : NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); - } else { - zend_execute_internal(execute_data, NULL, RETURN_VALUE_USED(opline) TSRMLS_CC); - } + if (EXPECTED(EG(exception) == NULL)) { + temp_variable *ret = &EX_T(opline->result.var); - if (!RETURN_VALUE_USED(opline)) { - zval_ptr_dtor(&ret->var.ptr); + MAKE_STD_ZVAL(ret->var.ptr); + ZVAL_NULL(ret->var.ptr); + ret->var.ptr_ptr = &ret->var.ptr; + ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; + + if (!zend_execute_internal) { + /* saves one function call if zend_execute_internal is not used */ + fbc->internal_function.handler(opline->extended_value, ret->var.ptr, (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? &ret->var.ptr : NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); + } else { + zend_execute_internal(execute_data, NULL, RETURN_VALUE_USED(opline) TSRMLS_CC); + } + + if (!RETURN_VALUE_USED(opline)) { + zval_ptr_dtor(&ret->var.ptr); + } } } else if (fbc->type == ZEND_USER_FUNCTION) { EX(original_return_value) = EG(return_value_ptr_ptr); @@ -2843,17 +2845,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)) { @@ -2865,16 +2867,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); @@ -2890,10 +2882,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"); @@ -3278,7 +3266,7 @@ ZEND_VM_HANDLER(64, ZEND_RECV_INIT, ANY, CONST) zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, assignment_value, opline->extended_value TSRMLS_CC); var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->result.var TSRMLS_CC); - Z_DELREF_PP(var_ptr); + zval_ptr_dtor(var_ptr); *var_ptr = assignment_value; CHECK_EXCEPTION(); @@ -4249,13 +4237,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(); @@ -4266,8 +4254,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); @@ -4279,15 +4270,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; } @@ -4298,8 +4297,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); @@ -4334,16 +4333,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; @@ -4359,26 +4357,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 2fe18fbceec..cd28387271c 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -523,13 +523,6 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR LOAD_OPLINE(); if (fbc->type == ZEND_INTERNAL_FUNCTION) { - temp_variable *ret = &EX_T(opline->result.var); - - MAKE_STD_ZVAL(ret->var.ptr); - ZVAL_NULL(ret->var.ptr); - ret->var.ptr_ptr = &ret->var.ptr; - ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; - if (fbc->common.arg_info) { zend_uint i=0; zval **p = (zval**)EX(function_state).arguments; @@ -541,15 +534,24 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR } } - if (!zend_execute_internal) { - /* saves one function call if zend_execute_internal is not used */ - fbc->internal_function.handler(opline->extended_value, ret->var.ptr, (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? &ret->var.ptr : NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); - } else { - zend_execute_internal(execute_data, NULL, RETURN_VALUE_USED(opline) TSRMLS_CC); - } + if (EXPECTED(EG(exception) == NULL)) { + temp_variable *ret = &EX_T(opline->result.var); - if (!RETURN_VALUE_USED(opline)) { - zval_ptr_dtor(&ret->var.ptr); + MAKE_STD_ZVAL(ret->var.ptr); + ZVAL_NULL(ret->var.ptr); + ret->var.ptr_ptr = &ret->var.ptr; + ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; + + if (!zend_execute_internal) { + /* saves one function call if zend_execute_internal is not used */ + fbc->internal_function.handler(opline->extended_value, ret->var.ptr, (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? &ret->var.ptr : NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC); + } else { + zend_execute_internal(execute_data, NULL, RETURN_VALUE_USED(opline) TSRMLS_CC); + } + + if (!RETURN_VALUE_USED(opline)) { + zval_ptr_dtor(&ret->var.ptr); + } } } else if (fbc->type == ZEND_USER_FUNCTION) { EX(original_return_value) = EG(return_value_ptr_ptr); @@ -1408,7 +1410,7 @@ static int ZEND_FASTCALL ZEND_RECV_INIT_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, assignment_value, opline->extended_value TSRMLS_CC); var_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(execute_data, opline->result.var TSRMLS_CC); - Z_DELREF_PP(var_ptr); + zval_ptr_dtor(var_ptr); *var_ptr = assignment_value; CHECK_EXCEPTION(); @@ -2337,17 +2339,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)) { @@ -2359,16 +2361,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); @@ -2384,10 +2376,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"); @@ -7658,17 +7646,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)) { @@ -7680,16 +7668,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); @@ -7705,10 +7683,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"); @@ -12892,17 +12866,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)) { @@ -12914,16 +12888,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); @@ -12939,10 +12903,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"); @@ -13592,13 +13552,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(); @@ -13609,8 +13569,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); @@ -13622,15 +13585,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; } @@ -13641,8 +13612,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); @@ -13677,16 +13648,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; @@ -13702,26 +13672,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(); @@ -30538,17 +30488,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)) { @@ -30560,16 +30510,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); @@ -30585,10 +30525,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"); diff --git a/acinclude.m4 b/acinclude.m4 index aadf4c06ee8..11ff779ff19 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -2342,8 +2342,10 @@ AC_DEFUN([PHP_SETUP_OPENSSL],[ AC_MSG_ERROR([OpenSSL version 0.9.6 or greater required.]) fi - if test -n "$OPENSSL_LIBS" && test -n "$OPENSSL_INCS"; then + if test -n "$OPENSSL_LIBS"; then PHP_EVAL_LIBLINE($OPENSSL_LIBS, $1) + fi + if test -n "$OPENSSL_INCS"; then PHP_EVAL_INCLINE($OPENSSL_INCS) fi fi diff --git a/ext/com_dotnet/com_iterator.c b/ext/com_dotnet/com_iterator.c index ce4bdd67c04..ecf395b161f 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 ad928497439..5450370cd9f 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 ba34c75b206..7bf9a9d220b 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, -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, +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, 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, 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, /* 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 71958578de9..a073aa691ea 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 3af3fa42ede..efae0a1db8e 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 d8bbfc5a042..094c7fdf45b 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 e2cb724f761..253cb84ce6b 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 f4f02903d14..31f73514819 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 67c8e27f91c..cc52a23b254 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 a7d9339d13d..862e92e9615 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 fc1b6029e98..ea219f7c7c6 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 e031ac6ee35..e3b38bb5ffd 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 f0898665385..7a282ab6c2e 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-- 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 00000000000..f1f9843d5ee --- /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-- + +==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 00000000000..70565960efb --- /dev/null +++ b/ext/date/tests/bug53437_var2.phpt @@ -0,0 +1,80 @@ +--TEST-- +Bug #53437 DateInterval basic serialization +--FILE-- + +==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 00000000000..06f68df61ff --- /dev/null +++ b/ext/date/tests/bug53437_var3.phpt @@ -0,0 +1,45 @@ +--TEST-- +Bug #53437 DateInterval unserialize bad data +--FILE-- + +==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 efc09b50479..13778a00b2c 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 26de5102151..7013a3f97c5 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-- --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 00000000000..f93ba28ab1f --- /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-- + 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/dba/dba.c b/ext/dba/dba.c index 303d65c33b9..5273c05fabb 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -538,7 +538,6 @@ PHP_MINFO_FUNCTION(dba) */ static void php_dba_update(INTERNAL_FUNCTION_PARAMETERS, int mode) { - char *v; int val_len; zval *id; dba_info *info = NULL; diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c index f4183d2f9a4..6c8cf84e9fe 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/gd/config.m4 b/ext/gd/config.m4 index 2f71705862d..e6cc0368310 100644 --- a/ext/gd/config.m4 +++ b/ext/gd/config.m4 @@ -298,7 +298,7 @@ if test "$PHP_GD" = "yes"; then libgd/gdcache.c libgd/gdkanji.c libgd/wbmp.c libgd/gd_wbmp.c libgd/gdhelpers.c \ libgd/gd_topal.c libgd/gd_gif_in.c libgd/xbm.c libgd/gd_gif_out.c libgd/gd_security.c \ libgd/gd_filter.c libgd/gd_pixelate.c libgd/gd_arc.c libgd/gd_rotate.c libgd/gd_color.c \ - libgd/gd_transform.c libgd/gd_crop.c" + libgd/gd_transform.c libgd/gd_crop.c libgd/gd_interpolation.c libgd/gd_matrix.c" dnl check for fabsf and floorf which are available since C99 AC_CHECK_FUNCS(fabsf floorf) diff --git a/ext/gd/config.w32 b/ext/gd/config.w32 index 1815e440a96..e6fbc49ea23 100644 --- a/ext/gd/config.w32 +++ b/ext/gd/config.w32 @@ -48,7 +48,7 @@ if (PHP_GD != "no") { gd_io_file.c gd_io_ss.c gd_jpeg.c gdkanji.c gd_png.c gd_ss.c \ gdtables.c gd_topal.c gd_wbmp.c gdxpm.c wbmp.c xbm.c gd_security.c gd_transform.c \ gd_filter.c gd_pixelate.c gd_arc.c gd_rotate.c gd_color.c webpimg.c gd_webp.c \ - gd_crop.c", "gd"); + gd_crop.c gd_interpolation.c gd_matrix.c", "gd"); AC_DEFINE('HAVE_LIBGD', 1, 'GD support'); ADD_FLAG("CFLAGS_GD", " \ /D HAVE_GD_DYNAMIC_CTX_EX=1 \ diff --git a/ext/gd/gd.c b/ext/gd/gd.c index be9501e5548..d929e7f84ef 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -58,7 +58,6 @@ # include "libgd/gd_compat.h" #endif - static int le_gd, le_gd_font; #if HAVE_LIBT1 #include @@ -195,6 +194,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagetruecolortopalette, 0) ZEND_ARG_INFO(0, colorsWanted) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_imagepalettetotruecolor, 0) + ZEND_ARG_INFO(0, im) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO(arginfo_imagecolormatch, 0) ZEND_ARG_INFO(0, im1) ZEND_ARG_INFO(0, im2) @@ -902,6 +905,35 @@ ZEND_BEGIN_ARG_INFO(arginfo_imagecropauto, 0) ZEND_ARG_INFO(0, threshold) ZEND_ARG_INFO(0, color) ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagescale, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, new_width) + ZEND_ARG_INFO(0, new_height) + ZEND_ARG_INFO(0, mode) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageaffine, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, affine) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageaffinematrixget, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, matrox) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imageaffinematrixconcat, 0) + ZEND_ARG_INFO(0, m1) + ZEND_ARG_INFO(0, m2) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_imagesetinterpolation, 0) + ZEND_ARG_INFO(0, im) + ZEND_ARG_INFO(0, method) +ZEND_END_ARG_INFO() + #endif /* }}} */ @@ -941,6 +973,7 @@ const zend_function_entry gd_functions[] = { PHP_FE(imagecreatetruecolor, arginfo_imagecreatetruecolor) PHP_FE(imageistruecolor, arginfo_imageistruecolor) PHP_FE(imagetruecolortopalette, arginfo_imagetruecolortopalette) + PHP_FE(imagepalettetotruecolor, arginfo_imagepalettetotruecolor) PHP_FE(imagesetthickness, arginfo_imagesetthickness) PHP_FE(imagefilledarc, arginfo_imagefilledarc) PHP_FE(imagefilledellipse, arginfo_imagefilledellipse) @@ -964,6 +997,11 @@ const zend_function_entry gd_functions[] = { PHP_FE(imageflip, arginfo_imageflip) PHP_FE(imagecrop, arginfo_imagecrop) PHP_FE(imagecropauto, arginfo_imagecropauto) + PHP_FE(imagescale, arginfo_imagescale) + PHP_FE(imageaffine, arginfo_imageaffine) + PHP_FE(imageaffinematrixconcat, arginfo_imageaffinematrixconcat) + PHP_FE(imageaffinematrixget, arginfo_imageaffinematrixget) + PHP_FE(imagesetinterpolation, arginfo_imagesetinterpolation) #endif #if HAVE_GD_IMAGESETTILE @@ -1218,13 +1256,43 @@ PHP_MINIT_FUNCTION(gd) REGISTER_LONG_CONSTANT("IMG_FLIP_HORIZONTAL", GD_FLIP_HORINZONTAL, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_FLIP_VERTICAL", GD_FLIP_VERTICAL, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_FLIP_BOTH", GD_FLIP_BOTH, CONST_CS | CONST_PERSISTENT); - + REGISTER_LONG_CONSTANT("IMG_CROP_DEFAULT", GD_CROP_DEFAULT, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_CROP_TRANSPARENT", GD_CROP_TRANSPARENT, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_CROP_BLACK", GD_CROP_BLACK, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_CROP_WHITE", GD_CROP_WHITE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_CROP_SIDES", GD_CROP_SIDES, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("IMG_CROP_THRESHOLD", GD_CROP_THRESHOLD, CONST_CS | CONST_PERSISTENT); + + + REGISTER_LONG_CONSTANT("IMG_BELL", GD_BILINEAR_FIXED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BESSEL", GD_BESSEL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BILINEAR_FIXED", GD_BILINEAR_FIXED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BICUBIC", GD_BICUBIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BICUBIC_FIXED", GD_BICUBIC_FIXED, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BLACKMAN", GD_BLACKMAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BOX", GD_BOX, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_BSPLINE", GD_BSPLINE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_CATMULLROM", GD_CATMULLROM, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_GAUSSIAN", GD_GAUSSIAN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_GENERALIZED_CUBIC", GD_GENERALIZED_CUBIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_HERMITE", GD_HERMITE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_HAMMING", GD_HAMMING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_HANNING", GD_HANNING, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_MITCHELL", GD_MITCHELL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_POWER", GD_POWER, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_QUADRATIC", GD_QUADRATIC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_SINC", GD_SINC, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_NEAREST_NEIGHBOUR", GD_NEAREST_NEIGHBOUR, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_WEIGHTED4", GD_WEIGHTED4, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_TRIANGLE", GD_TRIANGLE, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("IMG_AFFINE_TRANSLATE", GD_AFFINE_TRANSLATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_SCALE", GD_AFFINE_SCALE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_ROTATE", GD_AFFINE_ROTATE, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_SHEAR_HORIZONTAL", GD_AFFINE_SHEAR_HORIZONTAL, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("IMG_AFFINE_SHEAR_VERTICAL", GD_AFFINE_SHEAR_VERTICAL, CONST_CS | CONST_PERSISTENT); + #else REGISTER_LONG_CONSTANT("GD_BUNDLED", 0, CONST_CS | CONST_PERSISTENT); #endif @@ -1370,6 +1438,9 @@ PHP_MINFO_FUNCTION(gd) #endif #if defined(USE_GD_JISX0208) && defined(HAVE_GD_BUNDLED) php_info_print_table_row(2, "JIS-mapped Japanese Font Support", "enabled"); +#endif +#ifdef HAVE_GD_WEBP + php_info_print_table_row(2, "WebP Support", "enabled"); #endif php_info_print_table_end(); DISPLAY_INI_ENTRIES(); @@ -1730,6 +1801,29 @@ PHP_FUNCTION(imagetruecolortopalette) } /* }}} */ + + +/* {{{ proto void imagetruecolortopalette(resource im, bool ditherFlag, int colorsWanted) + Convert a true colour image to a palette based image with a number of colours, optionally using dithering. */ +PHP_FUNCTION(imagepalettetotruecolor) +{ + zval *IM; + gdImagePtr im; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &IM) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd); + + if (gdImagePaletteToTrueColor(im) == 0) { + RETURN_FALSE; + } + + RETURN_TRUE; +} +/* }}} */ + /* {{{ proto bool imagecolormatch(resource im1, resource im2) Makes the colors of the palette version of an image more closely match the true color version */ PHP_FUNCTION(imagecolormatch) @@ -2172,7 +2266,7 @@ PHP_FUNCTION(imagerotate) ZEND_FETCH_RESOURCE(im_src, gdImagePtr, &SIM, -1, "Image", le_gd); - im_dst = gdImageRotate(im_src, degrees, color, ignoretransparent); + im_dst = gdImageRotateInterpolated(im_src, (float)degrees, color); if (im_dst != NULL) { ZEND_REGISTER_RESOURCE(return_value, im_dst, le_gd); @@ -5127,11 +5221,11 @@ PHP_FUNCTION(imageflip) switch (mode) { case GD_FLIP_VERTICAL: - gdImageFlipHorizontal(im); + gdImageFlipVertical(im); break; case GD_FLIP_HORINZONTAL: - gdImageFlipVertical(im); + gdImageFlipHorizontal(im); break; case GD_FLIP_BOTH: @@ -5252,6 +5346,327 @@ PHP_FUNCTION(imagecropauto) } } /* }}} */ + +/* {{{ proto resource imagescale(resource im, new_width[, new_height[, method]]) + Crop an image using the given coordinates and size, x, y, width and height. */ +PHP_FUNCTION(imagescale) +{ + zval *IM; + long mode = -1; + long color = -1; + double threshold = 0.5f; + gdImagePtr im; + gdImagePtr im_scaled; + int new_width, new_height = -1; + gdInterpolationMethod method = GD_BILINEAR_FIXED; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|ll", &IM, &new_width, &new_height, &method) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd); + im_scaled = gdImageScale(im, new_width, new_height); + goto finish; + switch (method) { + case GD_NEAREST_NEIGHBOUR: + im_scaled = gdImageScaleNearestNeighbour(im, new_width, new_height); + break; + + case GD_BILINEAR_FIXED: + im_scaled = gdImageScaleBilinear(im, new_width, new_height); + break; + + case GD_BICUBIC: + im_scaled = gdImageScaleBicubicFixed(im, new_width, new_height); + break; + + case GD_BICUBIC_FIXED: + im_scaled = gdImageScaleBicubicFixed(im, new_width, new_height); + break; + + default: + im_scaled = gdImageScaleTwoPass(im, im->sx, im->sy, new_width, new_height); + break; + + } +finish: + if (im_scaled == NULL) { + RETURN_FALSE; + } else { + ZEND_REGISTER_RESOURCE(return_value, im_scaled, le_gd); + } +} +/* }}} */ + +/* {{{ proto resource imageaffine(resource dst, resource src, array affine, array clip) + Return an image containing the affine tramsformed src image, using an optional clipping area */ +PHP_FUNCTION(imageaffine) +{ + zval *IM; + long mode = -1; + long color = -1; + double threshold = 0.5f; + gdImagePtr src; + gdImagePtr dst; + gdRect rect; + gdRectPtr pRect = NULL; + zval *z_rect = NULL; + zval *z_affine; + zval **tmp; + double affine[6]; + int i, nelems; + zval **zval_affine_elem = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra|a", &IM, &z_affine, &z_rect) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(src, gdImagePtr, &IM, -1, "Image", le_gd); + + if ((nelems = zend_hash_num_elements(Z_ARRVAL_P(z_affine))) != 6) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Affine array must have six elements"); + RETURN_FALSE; + } + + for (i = 0; i < nelems; i++) { + if (zend_hash_index_find(Z_ARRVAL_P(z_affine), i, (void **) &zval_affine_elem) == SUCCESS) { + switch (Z_TYPE_PP(zval_affine_elem)) { + case IS_LONG: + affine[i] = Z_LVAL_PP(zval_affine_elem); + break; + case IS_DOUBLE: + affine[i] = Z_DVAL_PP(zval_affine_elem); + break; + case IS_STRING: + convert_to_double_ex(zval_affine_elem); + affine[i] = Z_DVAL_PP(zval_affine_elem); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %i", i); + RETURN_FALSE; + } + } + } + + if (z_rect != NULL) { + if (zend_hash_find(HASH_OF(z_rect), "x", sizeof("x"), (void **)&tmp) != FAILURE) { + convert_to_long_ex(tmp); + rect.x = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing x position"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(z_rect), "y", sizeof("x"), (void **)&tmp) != FAILURE) { + convert_to_long_ex(tmp); + rect.y = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing y position"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(z_rect), "width", sizeof("width"), (void **)&tmp) != FAILURE) { + convert_to_long_ex(tmp); + rect.width = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing width"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(z_rect), "height", sizeof("height"), (void **)&tmp) != FAILURE) { + convert_to_long_ex(tmp); + rect.height = Z_LVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing height"); + RETURN_FALSE; + } + pRect = ▭ + } else { + rect.x = -1; + rect.y = -1; + rect.width = gdImageSX(src); + rect.height = gdImageSY(src); + pRect = NULL; + } + + + //int gdTransformAffineGetImage(gdImagePtr *dst, const gdImagePtr src, gdRectPtr src_area, const double affine[6]); + if (gdTransformAffineGetImage(&dst, src, pRect, affine) != GD_TRUE) { + RETURN_FALSE; + } + + if (dst == NULL) { + RETURN_FALSE; + } else { + ZEND_REGISTER_RESOURCE(return_value, dst, le_gd); + } +} +/* }}} */ + +/* {{{ proto array imageaffinematrixget(type[, options]) + Return an image containing the affine tramsformed src image, using an optional clipping area */ +PHP_FUNCTION(imageaffinematrixget) +{ + double affine[6]; + gdAffineStandardMatrix type; + zval *options; + zval **tmp; + int res, i; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|z", &type, &options) == FAILURE) { + return; + } + + switch(type) { + case GD_AFFINE_TRANSLATE: + case GD_AFFINE_SCALE: { + double x, y; + if (Z_TYPE_P(options) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array expected as options"); + } + if (zend_hash_find(HASH_OF(options), "x", sizeof("x"), (void **)&tmp) != FAILURE) { + convert_to_double_ex(tmp); + x = Z_DVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing x position"); + RETURN_FALSE; + } + + if (zend_hash_find(HASH_OF(options), "y", sizeof("y"), (void **)&tmp) != FAILURE) { + convert_to_double_ex(tmp); + y = Z_DVAL_PP(tmp); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing y position"); + RETURN_FALSE; + } + + if (type == GD_AFFINE_TRANSLATE) { + res = gdAffineTranslate(affine, x, y); + } else { + res = gdAffineScale(affine, x, y); + } + break; + } + + case GD_AFFINE_ROTATE: + case GD_AFFINE_SHEAR_HORIZONTAL: + case GD_AFFINE_SHEAR_VERTICAL: { + double angle; + + convert_to_double_ex(&options); + angle = Z_DVAL_P(options); + + if (type == GD_AFFINE_SHEAR_HORIZONTAL) { + res = gdAffineShearHorizontal(affine, angle); + } else if (type == GD_AFFINE_SHEAR_VERTICAL) { + res = gdAffineShearVertical(affine, angle); + } else { + res = gdAffineRotate(affine, angle); + } + break; + } + + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %i", type); + RETURN_FALSE; + } + + array_init(return_value); + for (i = 0; i < 6; i++) { + add_index_double(return_value, i, affine[i]); + } +} + + +/* {{{ proto array imageaffineconcat(array m1, array m2) + Concat two matrices (as in doing many ops in one go) */ +PHP_FUNCTION(imageaffinematrixconcat) +{ + double m1[6]; + double m2[6]; + double mr[6]; + + zval **tmp; + zval *z_m1; + zval *z_m2; + int i, nelems; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "aa", &z_m1, &z_m2) == FAILURE) { + return; + } + + if (((nelems = zend_hash_num_elements(Z_ARRVAL_P(z_m1))) != 6) || (nelems = zend_hash_num_elements(Z_ARRVAL_P(z_m2))) != 6) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Affine arrays must have six elements"); + RETURN_FALSE; + } + + for (i = 0; i < 6; i++) { + if (zend_hash_index_find(Z_ARRVAL_P(z_m1), i, (void **) &tmp) == SUCCESS) { + switch (Z_TYPE_PP(tmp)) { + case IS_LONG: + m1[i] = Z_LVAL_PP(tmp); + break; + case IS_DOUBLE: + m1[i] = Z_DVAL_PP(tmp); + break; + case IS_STRING: + convert_to_double_ex(tmp); + m1[i] = Z_DVAL_PP(tmp); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %i", i); + RETURN_FALSE; + } + } + if (zend_hash_index_find(Z_ARRVAL_P(z_m2), i, (void **) &tmp) == SUCCESS) { + switch (Z_TYPE_PP(tmp)) { + case IS_LONG: + m2[i] = Z_LVAL_PP(tmp); + break; + case IS_DOUBLE: + m2[i] = Z_DVAL_PP(tmp); + break; + case IS_STRING: + convert_to_double_ex(tmp); + m2[i] = Z_DVAL_PP(tmp); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type for element %i", i); + RETURN_FALSE; + } + } + } + + if (gdAffineConcat (mr, m1, m2) != GD_TRUE) { + RETURN_FALSE; + } + + array_init(return_value); + for (i = 0; i < 6; i++) { + add_index_double(return_value, i, mr[i]); + } +} + +/* {{{ proto resource imagesetinterpolation(resource im, [, method]]) + Set the default interpolation method, passing -1 or 0 sets it to the libgd default (bilinear). */ +PHP_FUNCTION(imagesetinterpolation) +{ + zval *IM; + gdImagePtr im; + gdInterpolationMethod method = GD_BILINEAR_FIXED; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &IM, &method) == FAILURE) { + return; + } + + ZEND_FETCH_RESOURCE(im, gdImagePtr, &IM, -1, "Image", le_gd); + + if (method == -1) { + method = GD_BILINEAR_FIXED; + } + RETURN_BOOL(gdImageSetInterpolationMethod(im, (gdInterpolationMethod) method)); +} +/* }}} */ #endif diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index fa75898ddb3..cb45c0e9c27 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -168,6 +168,8 @@ gdImagePtr gdImageCreate (int sx, int sy) im->cy1 = 0; im->cx2 = im->sx - 1; im->cy2 = im->sy - 1; + im->interpolation = NULL; + im->interpolation_id = GD_BILINEAR_FIXED; return im; } @@ -183,7 +185,7 @@ gdImagePtr gdImageCreateTrueColor (int sx, int sy) if (overflow2(sizeof(unsigned char *), sy)) { return NULL; } - + if (overflow2(sizeof(int), sx)) { return NULL; } @@ -221,6 +223,8 @@ gdImagePtr gdImageCreateTrueColor (int sx, int sy) im->cy1 = 0; im->cx2 = im->sx - 1; im->cy2 = im->sy - 1; + im->interpolation = NULL; + im->interpolation_id = GD_BILINEAR_FIXED; return im; } @@ -3009,3 +3013,70 @@ void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P) *y2P = im->cy2; } +/* convert a palette image to true color */ +int gdImagePaletteToTrueColor(gdImagePtr src) +{ + unsigned int y; + unsigned char alloc_y = 0, alloc_aa = 0; + unsigned int yy; + + if (src == NULL) { + return 0; + } + + if (src->trueColor == 1) { + return 1; + } else { + unsigned int x; + const unsigned int sy = gdImageSY(src); + const unsigned int sx = gdImageSX(src); + + src->tpixels = (int **) gdMalloc(sizeof(int *) * sy); + if (src->tpixels == NULL) { + return 0; + } + + for (y = 0; y < sy; y++) { + const unsigned char *src_row = src->pixels[y]; + int * dst_row; + + /* no need to calloc it, we overwrite all pxl anyway */ + src->tpixels[y] = (int *) gdMalloc(sx * sizeof(int)); + if (src->tpixels[y] == NULL) { + goto clean_on_error; + } + + dst_row = src->tpixels[y]; + for (x = 0; x < sx; x++) { + const unsigned char c = *(src_row + x); + if (c == src->transparent) { + *(dst_row + x) = gdTrueColorAlpha(0, 0, 0, 127);; + } else { + *(dst_row + x) = gdTrueColorAlpha(src->red[c], src->green[c], src->blue[c], src->alpha[c]); + } + } + } + } + + /* free old palette buffer */ + for (yy = y - 1; yy >= yy - 1; yy--) { + gdFree(src->pixels[yy]); + } + gdFree(src->pixels); + src->trueColor = 1; + src->pixels = NULL; + src->alphaBlendingFlag = 0; + src->saveAlphaFlag = 1; + return 1; + +clean_on_error: + if (y > 0) { + + for (yy = y; yy >= yy - 1; y--) { + gdFree(src->tpixels[y]); + } + gdFree(src->tpixels); + } + return 0; +} + diff --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h index 8d9df2a49cf..0bd8ad336d8 100644 --- a/ext/gd/libgd/gd.h +++ b/ext/gd/libgd/gd.h @@ -93,6 +93,10 @@ void php_gd_error(const char *format, ...); #define gdEffectNormal 2 #define gdEffectOverlay 3 +#define GD_TRUE 1 +#define GD_FALSE 0 + +#define GD_EPSILON 1e-6 /* This function accepts truecolor pixel values only. The source color is composited with the destination color @@ -101,6 +105,67 @@ void php_gd_error(const char *format, ...); int gdAlphaBlend(int dest, int src); +/** + * Group: Transform + * + * Constants: gdInterpolationMethod + + * GD_BELL - Bell + * GD_BESSEL - Bessel + * GD_BILINEAR_FIXED - fixed point bilinear + * GD_BICUBIC - Bicubic + * GD_BICUBIC_FIXED - fixed point bicubic integer + * GD_BLACKMAN - Blackman + * GD_BOX - Box + * GD_BSPLINE - BSpline + * GD_CATMULLROM - Catmullrom + * GD_GAUSSIAN - Gaussian + * GD_GENERALIZED_CUBIC - Generalized cubic + * GD_HERMITE - Hermite + * GD_HAMMING - Hamming + * GD_HANNING - Hannig + * GD_MITCHELL - Mitchell + * GD_NEAREST_NEIGHBOUR - Nearest neighbour interpolation + * GD_POWER - Power + * GD_QUADRATIC - Quadratic + * GD_SINC - Sinc + * GD_TRIANGLE - Triangle + * GD_WEIGHTED4 - 4 pixels weighted bilinear interpolation + * + * See also: + * + **/ +typedef enum { + GD_DEFAULT = 0, + GD_BELL, + GD_BESSEL, + GD_BILINEAR_FIXED, + GD_BICUBIC, + GD_BICUBIC_FIXED, + GD_BLACKMAN, + GD_BOX, + GD_BSPLINE, + GD_CATMULLROM, + GD_GAUSSIAN, + GD_GENERALIZED_CUBIC, + GD_HERMITE, + GD_HAMMING, + GD_HANNING, + GD_MITCHELL, + GD_NEAREST_NEIGHBOUR, + GD_POWER, + GD_QUADRATIC, + GD_SINC, + GD_TRIANGLE, + GD_WEIGHTED4, + GD_METHOD_COUNT = 21 +} gdInterpolationMethod; + +/* define struct with name and func ptr and add it to gdImageStruct gdInterpolationMethod interpolation; */ + +/* Interpolation function ptr */ +typedef double (* interpolation_method )(double); + typedef struct gdImageStruct { /* Palette-based image pixels */ unsigned char ** pixels; @@ -188,10 +253,35 @@ typedef struct gdImageStruct { int cy1; int cx2; int cy2; + gdInterpolationMethod interpolation_id; + interpolation_method interpolation; } gdImage; typedef gdImage * gdImagePtr; +/* Point type for use in polygon drawing. */ + +/** + * Group: Types + * + * typedef: gdPointF + * Defines a point in a 2D coordinate system using floating point + * values. + * x - Floating point position (increase from left to right) + * y - Floating point Row position (increase from top to bottom) + * + * typedef: gdPointFPtr + * Pointer to a + * + * See also: + * , , + **/ +typedef struct +{ + double x, y; +} +gdPointF, *gdPointFPtr; + typedef struct { /* # of characters in font */ int nchars; @@ -469,7 +559,7 @@ void gdImageColorDeallocate(gdImagePtr im, int color); gdImagePtr gdImageCreatePaletteFromTrueColor (gdImagePtr im, int ditherFlag, int colorsWanted); void gdImageTrueColorToPalette(gdImagePtr im, int ditherFlag, int colorsWanted); - +int gdImagePaletteToTrueColor(gdImagePtr src); /* An attempt at getting the results of gdImageTrueColorToPalette to look a bit more like the original (im1 is the original @@ -600,6 +690,7 @@ gdImagePtr gdImageRotate180(gdImagePtr src, int ignoretransparent); gdImagePtr gdImageRotate270(gdImagePtr src, int ignoretransparent); gdImagePtr gdImageRotate45(gdImagePtr src, double dAngle, int clrBack, int ignoretransparent); gdImagePtr gdImageRotate (gdImagePtr src, double dAngle, int clrBack, int ignoretransparent); +gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor); void gdImageSetBrush(gdImagePtr im, gdImagePtr brush); void gdImageSetTile(gdImagePtr im, gdImagePtr tile); @@ -741,6 +832,54 @@ gdImagePtr gdImageCrop(gdImagePtr src, const gdRectPtr crop); gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode); gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const float threshold); +int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id); + +gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height); +gdImagePtr gdImageScaleBicubic(gdImagePtr src_img, const unsigned int new_width, const unsigned int new_height); +gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height); +gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height); +gdImagePtr gdImageScaleTwoPass(const gdImagePtr pOrigImage, const unsigned int uOrigWidth, const unsigned int uOrigHeight, const unsigned int uNewWidth, const unsigned int uNewHeight); +gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height); + +gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor); +gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor); +gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor); +gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor); + + + +typedef enum { + GD_AFFINE_TRANSLATE = 0, + GD_AFFINE_SCALE, + GD_AFFINE_ROTATE, + GD_AFFINE_SHEAR_HORIZONTAL, + GD_AFFINE_SHEAR_VERTICAL, +} gdAffineStandardMatrix; + +int gdAffineApplyToPointF (gdPointFPtr dst, const gdPointFPtr src, const double affine[6]); +int gdAffineInvert (double dst[6], const double src[6]); +int gdAffineFlip (double dst_affine[6], const double src_affine[6], const int flip_h, const int flip_v); +int gdAffineConcat (double dst[6], const double m1[6], const double m2[6]); + +int gdAffineIdentity (double dst[6]); +int gdAffineScale (double dst[6], const double scale_x, const double scale_y); +int gdAffineRotate (double dst[6], const double angle); +int gdAffineShearHorizontal (double dst[6], const double angle); +int gdAffineShearVertical(double dst[6], const double angle); +int gdAffineTranslate (double dst[6], const double offset_x, const double offset_y); +double gdAffineExpansion (const double src[6]); +int gdAffineRectilinear (const double src[6]); +int gdAffineEqual (const double matrix1[6], const double matrix2[6]); +int gdTransformAffineGetImage(gdImagePtr *dst, const gdImagePtr src, gdRectPtr src_area, const double affine[6]); +int gdTransformAffineCopy(gdImagePtr dst, int dst_x, int dst_y, const gdImagePtr src, gdRectPtr src_region, const double affine[6]); +/* +gdTransformAffineCopy(gdImagePtr dst, int x0, int y0, int x1, int y1, + const gdImagePtr src, int src_width, int src_height, + const double affine[6]); +*/ +int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox); + + #define GD_CMP_IMAGE 1 /* Actual image IS different */ #define GD_CMP_NUM_COLORS 2 /* Number of Colours in pallette differ */ #define GD_CMP_COLOR 4 /* Image colours differ */ diff --git a/ext/gd/libgd/gd_interpolation.c b/ext/gd/libgd/gd_interpolation.c new file mode 100644 index 00000000000..d805ec93e6c --- /dev/null +++ b/ext/gd/libgd/gd_interpolation.c @@ -0,0 +1,2553 @@ +/* + * Filtered Image Rescaling + * Based on Gems III + * - Schumacher general filtered image rescaling + * (pp. 414-424) + * by Dale Schumacher + * + * Additional changes by Ray Gardener, Daylon Graphics Ltd. + * December 4, 1999 + * + * Ported to libgd by Pierre Joye. Support for multiple channels + * added (argb for now). + * + * Initial sources code is avaibable in the Gems Source Code Packages: + * http://www.acm.org/pubs/tog/GraphicsGems/GGemsIII.tar.gz + */ + +/* + Summary: + + - Horizontal filter contributions are calculated on the fly, + as each column is mapped from src to dst image. This lets + us omit having to allocate a temporary full horizontal stretch + of the src image. + + - If none of the src pixels within a sampling region differ, + then the output pixel is forced to equal (any of) the source pixel. + This ensures that filters do not corrupt areas of constant color. + + - Filter weight contribution results, after summing, are + rounded to the nearest pixel color value instead of + being casted to ILubyte (usually an int or char). Otherwise, + artifacting occurs. + +*/ + +/* +TODO: + - Optimize pixel accesses and loops once we have continuous buffer + - Add scale support for a portion only of an image (equivalent of copyresized/resampled) + */ + +#include +#include +#include +#include + +#include +#include "gdhelpers.h" + +#ifdef _MSC_VER +# pragma optimize("t", on) +# include +#endif + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +#define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c))) +#ifndef MAX +#define MAX(a,b) ((a)<(b)?(b):(a)) +#endif +#define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c))) + +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +/* only used here, let do a generic fixed point integers later if required by other + part of GD */ +typedef long gdFixed; +/* Integer to fixed point */ +#define gd_itofx(x) ((x) << 8) + +/* Float to fixed point */ +#define gd_ftofx(x) (long)((x) * 256) + +/* Double to fixed point */ +#define gd_dtofx(x) (long)((x) * 256) + +/* Fixed point to integer */ +#define gd_fxtoi(x) ((x) >> 8) + +/* Fixed point to float */ +# define gd_fxtof(x) ((float)(x) / 256) + +/* Fixed point to double */ +#define gd_fxtod(x) ((double)(x) / 256) + +/* Multiply a fixed by a fixed */ +#define gd_mulfx(x,y) (((x) * (y)) >> 8) + +/* Divide a fixed by a fixed */ +#define gd_divfx(x,y) (((x) << 8) / (y)) + +typedef struct +{ + double *Weights; /* Normalized weights of neighboring pixels */ + int Left,Right; /* Bounds of source pixels window */ +} ContributionType; /* Contirbution information for a single pixel */ + +typedef struct +{ + ContributionType *ContribRow; /* Row (or column) of contribution weights */ + unsigned int WindowSize, /* Filter window size (of affecting source pixels) */ + LineLength; /* Length of line (no. or rows / cols) */ +} LineContribType; + +/* Each core filter has its own radius */ +#define DEFAULT_FILTER_BICUBIC 3.0 +#define DEFAULT_FILTER_BOX 0.5 +#define DEFAULT_FILTER_GENERALIZED_CUBIC 0.5 +#define DEFAULT_FILTER_RADIUS 1.0 +#define DEFAULT_LANCZOS8_RADIUS 8.0 +#define DEFAULT_LANCZOS3_RADIUS 3.0 +#define DEFAULT_HERMITE_RADIUS 1.0 +#define DEFAULT_BOX_RADIUS 0.5 +#define DEFAULT_TRIANGLE_RADIUS 1.0 +#define DEFAULT_BELL_RADIUS 1.5 +#define DEFAULT_CUBICSPLINE_RADIUS 2.0 +#define DEFAULT_MITCHELL_RADIUS 2.0 +#define DEFAULT_COSINE_RADIUS 1.0 +#define DEFAULT_CATMULLROM_RADIUS 2.0 +#define DEFAULT_QUADRATIC_RADIUS 1.5 +#define DEFAULT_QUADRATICBSPLINE_RADIUS 1.5 +#define DEFAULT_CUBICCONVOLUTION_RADIUS 3.0 +#define DEFAULT_GAUSSIAN_RADIUS 1.0 +#define DEFAULT_HANNING_RADIUS 1.0 +#define DEFAULT_HAMMING_RADIUS 1.0 +#define DEFAULT_SINC_RADIUS 1.0 +#define DEFAULT_WELSH_RADIUS 1.0 + +enum GD_RESIZE_FILTER_TYPE{ + FILTER_DEFAULT = 0, + FILTER_BELL, + FILTER_BESSEL, + FILTER_BLACKMAN, + FILTER_BOX, + FILTER_BSPLINE, + FILTER_CATMULLROM, + FILTER_COSINE, + FILTER_CUBICCONVOLUTION, + FILTER_CUBICSPLINE, + FILTER_HERMITE, + FILTER_LANCZOS3, + FILTER_LANCZOS8, + FILTER_MITCHELL, + FILTER_QUADRATIC, + FILTER_QUADRATICBSPLINE, + FILTER_TRIANGLE, + FILTER_GAUSSIAN, + FILTER_HANNING, + FILTER_HAMMING, + FILTER_SINC, + FILTER_WELSH, + + FILTER_CALLBACK = 999 +}; + +typedef enum GD_RESIZE_FILTER_TYPE gdResizeFilterType; + +static double KernelBessel_J1(const double x) +{ + double p, q; + + register long i; + + static const double + Pone[] = + { + 0.581199354001606143928050809e+21, + -0.6672106568924916298020941484e+20, + 0.2316433580634002297931815435e+19, + -0.3588817569910106050743641413e+17, + 0.2908795263834775409737601689e+15, + -0.1322983480332126453125473247e+13, + 0.3413234182301700539091292655e+10, + -0.4695753530642995859767162166e+7, + 0.270112271089232341485679099e+4 + }, + Qone[] = + { + 0.11623987080032122878585294e+22, + 0.1185770712190320999837113348e+20, + 0.6092061398917521746105196863e+17, + 0.2081661221307607351240184229e+15, + 0.5243710262167649715406728642e+12, + 0.1013863514358673989967045588e+10, + 0.1501793594998585505921097578e+7, + 0.1606931573481487801970916749e+4, + 0.1e+1 + }; + + p = Pone[8]; + q = Qone[8]; + for (i=7; i >= 0; i--) + { + p = p*x*x+Pone[i]; + q = q*x*x+Qone[i]; + } + return (double)(p/q); +} + +static double KernelBessel_P1(const double x) +{ + double p, q; + + register long i; + + static const double + Pone[] = + { + 0.352246649133679798341724373e+5, + 0.62758845247161281269005675e+5, + 0.313539631109159574238669888e+5, + 0.49854832060594338434500455e+4, + 0.2111529182853962382105718e+3, + 0.12571716929145341558495e+1 + }, + Qone[] = + { + 0.352246649133679798068390431e+5, + 0.626943469593560511888833731e+5, + 0.312404063819041039923015703e+5, + 0.4930396490181088979386097e+4, + 0.2030775189134759322293574e+3, + 0.1e+1 + }; + + p = Pone[5]; + q = Qone[5]; + for (i=4; i >= 0; i--) + { + p = p*(8.0/x)*(8.0/x)+Pone[i]; + q = q*(8.0/x)*(8.0/x)+Qone[i]; + } + return (double)(p/q); +} + +static double KernelBessel_Q1(const double x) +{ + double p, q; + + register long i; + + static const double + Pone[] = + { + 0.3511751914303552822533318e+3, + 0.7210391804904475039280863e+3, + 0.4259873011654442389886993e+3, + 0.831898957673850827325226e+2, + 0.45681716295512267064405e+1, + 0.3532840052740123642735e-1 + }, + Qone[] = + { + 0.74917374171809127714519505e+4, + 0.154141773392650970499848051e+5, + 0.91522317015169922705904727e+4, + 0.18111867005523513506724158e+4, + 0.1038187585462133728776636e+3, + 0.1e+1 + }; + + p = Pone[5]; + q = Qone[5]; + for (i=4; i >= 0; i--) + { + p = p*(8.0/x)*(8.0/x)+Pone[i]; + q = q*(8.0/x)*(8.0/x)+Qone[i]; + } + return (double)(p/q); +} + +static double KernelBessel_Order1(double x) +{ + double p, q; + + if (x == 0.0) + return (0.0f); + p = x; + if (x < 0.0) + x=(-x); + if (x < 8.0) + return (p*KernelBessel_J1(x)); + q = (double)sqrt(2.0f/(M_PI*x))*(double)(KernelBessel_P1(x)*(1.0f/sqrt(2.0f)*(sin(x)-cos(x)))-8.0f/x*KernelBessel_Q1(x)* + (-1.0f/sqrt(2.0f)*(sin(x)+cos(x)))); + if (p < 0.0f) + q = (-q); + return (q); +} + +static double filter_bessel(const double x) +{ + if (x == 0.0f) + return (double)(M_PI/4.0f); + return (KernelBessel_Order1((double)M_PI*x)/(2.0f*x)); +} + + +static double filter_blackman(const double x) +{ + return (0.42f+0.5f*(double)cos(M_PI*x)+0.08f*(double)cos(2.0f*M_PI*x)); +} + +/** + * Bicubic interpolation kernel (a=-1): + \verbatim + / + | 1-2|t|**2+|t|**3 , if |t| < 1 + h(t) = | 4-8|t|+5|t|**2-|t|**3 , if 1<=|t|<2 + | 0 , otherwise + \ + \endverbatim + * ***bd*** 2.2004 + */ +static double filter_bicubic(const double t) +{ + const double abs_t = (double)fabs(t); + const double abs_t_sq = abs_t * abs_t; + if (abs_t<1) return 1-2*abs_t_sq+abs_t_sq*abs_t; + if (abs_t<2) return 4 - 8*abs_t +5*abs_t_sq - abs_t_sq*abs_t; + return 0; +} + +/** + * Generalized cubic kernel (for a=-1 it is the same as BicubicKernel): + \verbatim + / + | (a+2)|t|**3 - (a+3)|t|**2 + 1 , |t| <= 1 + h(t) = | a|t|**3 - 5a|t|**2 + 8a|t| - 4a , 1 < |t| <= 2 + | 0 , otherwise + \ + \endverbatim + * Often used values for a are -1 and -1/2. + */ +static double filter_generalized_cubic(const double t) +{ + const double a = -DEFAULT_FILTER_GENERALIZED_CUBIC; + double abs_t = (double)fabs(t); + double abs_t_sq = abs_t * abs_t; + if (abs_t < 1) return (a + 2) * abs_t_sq * abs_t - (a + 3) * abs_t_sq + 1; + if (abs_t < 2) return a * abs_t_sq * abs_t - 5 * a * abs_t_sq + 8 * a * abs_t - 4 * a; + return 0; +} + +/* CubicSpline filter, default radius 2 */ +static double filter_cubic_spline(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x < 1.0 ) { + const double x2 = x*x; + + return (0.5 * x2 * x - x2 + 2.0 / 3.0); + } + if (x < 2.0) { + return (pow(2.0 - x, 3.0)/6.0); + } + return 0; +} + +/* CubicConvolution filter, default radius 3 */ +static double filter_cubic_convolution(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + const double x2 = x1 * x1; + const double x2_x = x2 * x; + + if (x <= 1.0) return ((4.0 / 3.0)* x2_x - (7.0 / 3.0) * x2 + 1.0); + if (x <= 2.0) return (- (7.0 / 12.0) * x2_x + 3 * x2 - (59.0 / 12.0) * x + 2.5); + if (x <= 3.0) return ( (1.0/12.0) * x2_x - (2.0 / 3.0) * x2 + 1.75 * x - 1.5); + return 0; +} + +static double filter_box(double x) { + if (x < - DEFAULT_FILTER_BOX) + return 0.0f; + if (x < DEFAULT_FILTER_BOX) + return 1.0f; + return 0.0f; +} + +static double filter_catmullrom(const double x) +{ + if (x < -2.0) + return(0.0f); + if (x < -1.0) + return(0.5f*(4.0f+x*(8.0f+x*(5.0f+x)))); + if (x < 0.0) + return(0.5f*(2.0f+x*x*(-5.0f-3.0f*x))); + if (x < 1.0) + return(0.5f*(2.0f+x*x*(-5.0f+3.0f*x))); + if (x < 2.0) + return(0.5f*(4.0f+x*(-8.0f+x*(5.0f-x)))); + return(0.0f); +} + +static double filter_filter(double t) +{ + /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ + if(t < 0.0) t = -t; + if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0); + return(0.0); +} + + +/* Lanczos8 filter, default radius 8 */ +static double filter_lanczos8(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; +#define R DEFAULT_LANCZOS8_RADIUS + + if ( x == 0.0) return 1; + + if ( x < R) { + return R * sin(x*M_PI) * sin(x * M_PI/ R) / (x * M_PI * x * M_PI); + } + return 0.0; +#undef R +} + + +/* Lanczos3 filter, default radius 3 */ +static double filter_lanczos3(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; +#define R DEFAULT_LANCZOS3_RADIUS + + if ( x == 0.0) return 1; + + if ( x < R) + { + return R * sin(x*M_PI) * sin(x * M_PI / R) / (x * M_PI * x * M_PI); + } + return 0.0; +#undef R +} + +/* Hermite filter, default radius 1 */ +static double filter_hermite(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x < 1.0) return ((2.0 * x - 3) * x * x + 1.0 ); + + return 0.0; +} + +/* Trangle filter, default radius 1 */ +static double filter_triangle(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + if (x < 1.0) return (1.0 - x); + return 0.0; +} + +/* Bell filter, default radius 1.5 */ +static double filter_bell(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x < 0.5) return (0.75 - x*x); + if (x < 1.5) return (0.5 * pow(x - 1.5, 2.0)); + return 0.0; +} + +/* Mitchell filter, default radius 2.0 */ +static double filter_mitchell(const double x) +{ +#define KM_B (1.0f/3.0f) +#define KM_C (1.0f/3.0f) +#define KM_P0 (( 6.0f - 2.0f * KM_B ) / 6.0f) +#define KM_P2 ((-18.0f + 12.0f * KM_B + 6.0f * KM_C) / 6.0f) +#define KM_P3 (( 12.0f - 9.0f * KM_B - 6.0f * KM_C) / 6.0f) +#define KM_Q0 (( 8.0f * KM_B + 24.0f * KM_C) / 6.0f) +#define KM_Q1 ((-12.0f * KM_B - 48.0f * KM_C) / 6.0f) +#define KM_Q2 (( 6.0f * KM_B + 30.0f * KM_C) / 6.0f) +#define KM_Q3 (( -1.0f * KM_B - 6.0f * KM_C) / 6.0f) + + if (x < -2.0) + return(0.0f); + if (x < -1.0) + return(KM_Q0-x*(KM_Q1-x*(KM_Q2-x*KM_Q3))); + if (x < 0.0f) + return(KM_P0+x*x*(KM_P2-x*KM_P3)); + if (x < 1.0f) + return(KM_P0+x*x*(KM_P2+x*KM_P3)); + if (x < 2.0f) + return(KM_Q0+x*(KM_Q1+x*(KM_Q2+x*KM_Q3))); + return(0.0f); +} + + + +/* Cosine filter, default radius 1 */ +static double filter_cosine(const double x) +{ + if ((x >= -1.0) && (x <= 1.0)) return ((cos(x * M_PI) + 1.0)/2.0); + + return 0; +} + +/* Quadratic filter, default radius 1.5 */ +static double filter_quadratic(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x <= 0.5) return (- 2.0 * x * x + 1); + if (x <= 1.5) return (x * x - 2.5* x + 1.5); + return 0.0; +} + +static double filter_bspline(const double x) +{ + if (x>2.0f) { + return 0.0f; + } else { + double a, b, c, d; + /* Was calculated anyway cause the "if((x-1.0f) < 0)" */ + const double xm1 = x - 1.0f; + const double xp1 = x + 1.0f; + const double xp2 = x + 2.0f; + + if ((xp2) <= 0.0f) a = 0.0f; else a = xp2*xp2*xp2; + if ((xp1) <= 0.0f) b = 0.0f; else b = xp1*xp1*xp1; + if (x <= 0) c = 0.0f; else c = x*x*x; + if ((xm1) <= 0.0f) d = 0.0f; else d = xm1*xm1*xm1; + + return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d))); + } +} + +/* QuadraticBSpline filter, default radius 1.5 */ +static double filter_quadratic_bspline(const double x1) +{ + const double x = x1 < 0.0 ? -x1 : x1; + + if (x <= 0.5) return (- x * x + 0.75); + if (x <= 1.5) return (0.5 * x * x - 1.5 * x + 1.125); + return 0.0; +} + +static double filter_gaussian(const double x) +{ + /* return(exp((double) (-2.0 * x * x)) * sqrt(2.0 / M_PI)); */ + return (double)(exp(-2.0f * x * x) * 0.79788456080287f); +} + +static double filter_hanning(const double x) +{ + /* A Cosine windowing function */ + return(0.5 + 0.5 * cos(M_PI * x)); +} + +static double filter_hamming(const double x) +{ + /* should be + (0.54+0.46*cos(M_PI*(double) x)); + but this approximation is sufficient */ + if (x < -1.0f) + return 0.0f; + if (x < 0.0f) + return 0.92f*(-2.0f*x-3.0f)*x*x+1.0f; + if (x < 1.0f) + return 0.92f*(2.0f*x-3.0f)*x*x+1.0f; + return 0.0f; +} + +static double filter_power(const double x) +{ + const double a = 2.0f; + if (fabs(x)>1) return 0.0f; + return (1.0f - (double)fabs(pow(x,a))); +} + +static double filter_sinc(const double x) +{ + /* X-scaled Sinc(x) function. */ + if (x == 0.0) return(1.0); + return (sin(M_PI * (double) x) / (M_PI * (double) x)); +} + +static double filter_welsh(const double x) +{ + /* Welsh parabolic windowing filter */ + if (x < 1.0) + return(1 - x*x); + return(0.0); +} + + +/* Copied from upstream's libgd */ +static inline int _color_blend (const int dst, const int src) +{ + const int src_alpha = gdTrueColorGetAlpha(src); + + if( src_alpha == gdAlphaOpaque ) { + return src; + } else { + const int dst_alpha = gdTrueColorGetAlpha(dst); + + if( src_alpha == gdAlphaTransparent ) return dst; + if( dst_alpha == gdAlphaTransparent ) { + return src; + } else { + register int alpha, red, green, blue; + const int src_weight = gdAlphaTransparent - src_alpha; + const int dst_weight = (gdAlphaTransparent - dst_alpha) * src_alpha / gdAlphaMax; + const int tot_weight = src_weight + dst_weight; + + alpha = src_alpha * dst_alpha / gdAlphaMax; + + red = (gdTrueColorGetRed(src) * src_weight + + gdTrueColorGetRed(dst) * dst_weight) / tot_weight; + green = (gdTrueColorGetGreen(src) * src_weight + + gdTrueColorGetGreen(dst) * dst_weight) / tot_weight; + blue = (gdTrueColorGetBlue(src) * src_weight + + gdTrueColorGetBlue(dst) * dst_weight) / tot_weight; + + return ((alpha << 24) + (red << 16) + (green << 8) + blue); + } + } +} + +static inline int _setEdgePixel(const gdImagePtr src, unsigned int x, unsigned int y, gdFixed coverage, const int bgColor) +{ + const gdFixed f_127 = gd_itofx(127); + register int c = src->tpixels[y][x]; + c = c | (( (int) (gd_fxtof(gd_mulfx(coverage, f_127)) + 50.5f)) << 24); + return _color_blend(bgColor, c); +} + +static inline int getPixelOverflowTC(gdImagePtr im, const int x, const int y, const int bgColor) +{ + if (gdImageBoundsSafe(im, x, y)) { + const int c = im->tpixels[y][x]; + if (c == im->transparent) { + return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor; + } + return c; + } else { + register int border; + + if (y < im->cy1) { + border = im->tpixels[0][im->cx1]; + goto processborder; + } + + if (y < im->cy1) { + border = im->tpixels[0][im->cx1]; + goto processborder; + } + + if (y > im->cy2) { + if (x >= im->cx1 && x <= im->cx1) { + border = im->tpixels[im->cy2][x]; + goto processborder; + } else { + return gdTrueColorAlpha(0, 0, 0, 127); + } + } + + /* y is bound safe at this point */ + if (x < im->cx1) { + border = im->tpixels[y][im->cx1]; + goto processborder; + } + + if (x > im->cx2) { + border = im->tpixels[y][im->cx2]; + } + +processborder: + if (border == im->transparent) { + return gdTrueColorAlpha(0, 0, 0, 127); + } else{ + return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127); + } + } +} + +#define colorIndex2RGBA(c) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(c)]) +#define colorIndex2RGBcustomA(c, a) gdTrueColorAlpha(im->red[(c)], im->green[(c)], im->blue[(c)], im->alpha[(a)]) +static inline int getPixelOverflowPalette(gdImagePtr im, const int x, const int y, const int bgColor) +{ + if (gdImageBoundsSafe(im, x, y)) { + const int c = im->pixels[y][x]; + if (c == im->transparent) { + return bgColor == -1 ? gdTrueColorAlpha(0, 0, 0, 127) : bgColor; + } + return colorIndex2RGBA(c); + } else { + register int border; + if (y < im->cy1) { + border = gdImageGetPixel(im, im->cx1, 0); + goto processborder; + } + + if (y < im->cy1) { + border = gdImageGetPixel(im, im->cx1, 0); + goto processborder; + } + + if (y > im->cy2) { + if (x >= im->cx1 && x <= im->cx1) { + border = gdImageGetPixel(im, x, im->cy2); + goto processborder; + } else { + return gdTrueColorAlpha(0, 0, 0, 127); + } + } + + /* y is bound safe at this point */ + if (x < im->cx1) { + border = gdImageGetPixel(im, im->cx1, y); + goto processborder; + } + + if (x > im->cx2) { + border = gdImageGetPixel(im, im->cx2, y); + } + +processborder: + if (border == im->transparent) { + return gdTrueColorAlpha(0, 0, 0, 127); + } else{ + return colorIndex2RGBcustomA(border, 127); + } + } +} + +static int getPixelInterpolateWeight(gdImagePtr im, const double x, const double y, const int bgColor) +{ + /* Closest pixel <= (xf,yf) */ + int sx = (int)(x); + int sy = (int)(y); + const double xf = x - (double)sx; + const double yf = y - (double)sy; + const double nxf = (double) 1.0 - xf; + const double nyf = (double) 1.0 - yf; + const double m1 = xf * yf; + const double m2 = nxf * yf; + const double m3 = xf * nyf; + const double m4 = nxf * nyf; + + /* get color values of neighbouring pixels */ + const int c1 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy, bgColor) : getPixelOverflowPalette(im, sx, sy, bgColor); + const int c2 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy, bgColor) : getPixelOverflowPalette(im, sx - 1, sy, bgColor); + const int c3 = im->trueColor == 1 ? getPixelOverflowTC(im, sx, sy - 1, bgColor) : getPixelOverflowPalette(im, sx, sy - 1, bgColor); + const int c4 = im->trueColor == 1 ? getPixelOverflowTC(im, sx - 1, sy - 1, bgColor) : getPixelOverflowPalette(im, sx, sy - 1, bgColor); + int r, g, b, a; + + if (x < 0) sx--; + if (y < 0) sy--; + + /* component-wise summing-up of color values */ + if (im->trueColor) { + r = (int)(m1*gdTrueColorGetRed(c1) + m2*gdTrueColorGetRed(c2) + m3*gdTrueColorGetRed(c3) + m4*gdTrueColorGetRed(c4)); + g = (int)(m1*gdTrueColorGetGreen(c1) + m2*gdTrueColorGetGreen(c2) + m3*gdTrueColorGetGreen(c3) + m4*gdTrueColorGetGreen(c4)); + b = (int)(m1*gdTrueColorGetBlue(c1) + m2*gdTrueColorGetBlue(c2) + m3*gdTrueColorGetBlue(c3) + m4*gdTrueColorGetBlue(c4)); + a = (int)(m1*gdTrueColorGetAlpha(c1) + m2*gdTrueColorGetAlpha(c2) + m3*gdTrueColorGetAlpha(c3) + m4*gdTrueColorGetAlpha(c4)); + } else { + r = (int)(m1*im->red[(c1)] + m2*im->red[(c2)] + m3*im->red[(c3)] + m4*im->red[(c4)]); + g = (int)(m1*im->green[(c1)] + m2*im->green[(c2)] + m3*im->green[(c3)] + m4*im->green[(c4)]); + b = (int)(m1*im->blue[(c1)] + m2*im->blue[(c2)] + m3*im->blue[(c3)] + m4*im->blue[(c4)]); + a = (int)(m1*im->alpha[(c1)] + m2*im->alpha[(c2)] + m3*im->alpha[(c3)] + m4*im->alpha[(c4)]); + } + + r = CLAMP(r, 0, 255); + g = CLAMP(g, 0, 255); + b = CLAMP(b, 0, 255); + a = CLAMP(a, 0, gdAlphaMax); + return gdTrueColorAlpha(r, g, b, a); +} + +/** + * Function: getPixelInterpolated + * Returns the interpolated color value using the default interpolation + * method. The returned color is always in the ARGB format (truecolor). + * + * Parameters: + * im - Image to set the default interpolation method + * y - X value of the ideal position + * y - Y value of the ideal position + * method - Interpolation method + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + * + * See also: + * + */ +int getPixelInterpolated(gdImagePtr im, const double x, const double y, const int bgColor) +{ + const int xi=(int)((x) < 0 ? x - 1: x); + const int yi=(int)((y) < 0 ? y - 1: y); + int yii; + int i; + double kernel, kernel_cache_y; + double kernel_x[12], kernel_y[4]; + double new_r = 0.0f, new_g = 0.0f, new_b = 0.0f, new_a = 0.0f; + + /* These methods use special implementations */ + if (im->interpolation_id == GD_BILINEAR_FIXED || im->interpolation_id == GD_BICUBIC_FIXED || im->interpolation_id == GD_NEAREST_NEIGHBOUR) { + return -1; + } + + /* Default to full alpha */ + if (bgColor == -1) { + } + + if (im->interpolation_id == GD_WEIGHTED4) { + return getPixelInterpolateWeight(im, x, y, bgColor); + } + + if (im->interpolation_id == GD_NEAREST_NEIGHBOUR) { + if (im->trueColor == 1) { + return getPixelOverflowTC(im, xi, yi, bgColor); + } else { + return getPixelOverflowPalette(im, xi, yi, bgColor); + } + } + if (im->interpolation) { + for (i=0; i<4; i++) { + kernel_x[i] = (double) im->interpolation((double)(xi+i-1-x)); + kernel_y[i] = (double) im->interpolation((double)(yi+i-1-y)); + } + } else { + return -1; + } + + /* + * TODO: use the known fast rgba multiplication implementation once + * the new formats are in place + */ + for (yii = yi-1; yii < yi+3; yii++) { + int xii; + kernel_cache_y = kernel_y[yii-(yi-1)]; + if (im->trueColor) { + for (xii=xi-1; xiiWindowSize = windows_size; + res->LineLength = line_length; + res->ContribRow = (ContributionType *) gdMalloc(line_length * sizeof(ContributionType)); + + for (u = 0 ; u < line_length ; u++) { + res->ContribRow[u].Weights = (double *) gdMalloc(windows_size * sizeof(double)); + } + return res; +} + +static inline _gdContributionsFree(LineContribType * p) +{ + unsigned int u; + for (u = 0; u < p->LineLength; u++) { + gdFree(p->ContribRow[u].Weights); + } + gdFree(p->ContribRow); + gdFree(p); +} + +static inline LineContribType *_gdContributionsCalc(unsigned int line_size, unsigned int src_size, double scale_d, const interpolation_method pFilter) +{ + double width_d; + double scale_f_d = 1.0; + const double filter_width_d = DEFAULT_BOX_RADIUS; + int windows_size; + unsigned int u; + LineContribType *res; + + if (scale_d < 1.0) { + width_d = filter_width_d / scale_d; + scale_f_d = scale_d; + } else { + width_d= filter_width_d; + } + + windows_size = 2 * (int)ceil(width_d) + 1; + res = _gdContributionsAlloc(line_size, windows_size); + + for (u = 0; u < line_size; u++) { + const double dCenter = (double)u / scale_d; + /* get the significant edge points affecting the pixel */ + register int iLeft = MAX(0, (int)floor (dCenter - width_d)); + int iRight = MIN((int)ceil(dCenter + width_d), (int)src_size - 1); + double dTotalWeight = 0.0; + int iSrc; + + res->ContribRow[u].Left = iLeft; + res->ContribRow[u].Right = iRight; + + /* Cut edge points to fit in filter window in case of spill-off */ + if (iRight - iLeft + 1 > windows_size) { + if (iLeft < ((int)src_size - 1 / 2)) { + iLeft++; + } else { + iRight--; + } + } + + for (iSrc = iLeft; iSrc <= iRight; iSrc++) { + dTotalWeight += (res->ContribRow[u].Weights[iSrc-iLeft] = scale_f_d * (*pFilter)(scale_f_d * (dCenter - (double)iSrc))); + } + + if (dTotalWeight < 0.0) { + _gdContributionsFree(res); + return NULL; + } + + if (dTotalWeight > 0.0) { + for (iSrc = iLeft; iSrc <= iRight; iSrc++) { + res->ContribRow[u].Weights[iSrc-iLeft] /= dTotalWeight; + } + } + } + return res; +} + +static inline void _gdScaleRow(gdImagePtr pSrc, unsigned int src_width, gdImagePtr dst, unsigned int dst_width, unsigned int row, LineContribType *contrib) +{ + int *p_src_row = pSrc->tpixels[row]; + int *p_dst_row = dst->tpixels[row]; + unsigned int x; + + for (x = 0; x < dst_width - 1; x++) { + register unsigned char r = 0, g = 0, b = 0, a = 0; + const int left = contrib->ContribRow[x].Left; + const int right = contrib->ContribRow[x].Right; + int i; + + /* Accumulate each channel */ + for (i = left; i <= right; i++) { + const left_channel = i - left; + r += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetRed(p_src_row[i]))); + g += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetGreen(p_src_row[i]))); + b += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetBlue(p_src_row[i]))); + a += (unsigned char)(contrib->ContribRow[x].Weights[left_channel] * (double)(gdTrueColorGetAlpha(p_src_row[i]))); + } + p_dst_row[x] = gdTrueColorAlpha(r, g, b, a); + } +} + +static inline void _gdScaleHoriz(gdImagePtr pSrc, unsigned int src_width, unsigned int src_height, gdImagePtr pDst, unsigned int dst_width, unsigned int dst_height) +{ + unsigned int u; + LineContribType * contrib; + + /* same width, just copy it */ + if (dst_width == src_width) { + unsigned int y; + for (y = 0; y < src_height - 1; ++y) { + memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width); + } + } + + contrib = _gdContributionsCalc(dst_width, src_width, (double)dst_width / (double)src_width, pSrc->interpolation); + if (contrib == NULL) { + return; + } + /* Scale each row */ + for (u = 0; u < dst_height - 1; u++) { + _gdScaleRow(pSrc, src_width, pDst, dst_width, u, contrib); + } + _gdContributionsFree (contrib); +} + +static inline _gdScaleCol (gdImagePtr pSrc, unsigned int src_width, gdImagePtr pRes, unsigned int dst_width, unsigned int dst_height, unsigned int uCol, LineContribType *contrib) +{ + unsigned int y; + for (y = 0; y < dst_height - 1; y++) { + register unsigned char r = 0, g = 0, b = 0, a = 0; + const int iLeft = contrib->ContribRow[y].Left; + const int iRight = contrib->ContribRow[y].Right; + int i; + int *row = pRes->tpixels[y]; + + /* Accumulate each channel */ + for (i = iLeft; i <= iRight; i++) { + const int pCurSrc = pSrc->tpixels[i][uCol]; + const int i_iLeft = i - iLeft; + r += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetRed(pCurSrc))); + g += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetGreen(pCurSrc))); + b += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetBlue(pCurSrc))); + a += (unsigned char)(contrib->ContribRow[y].Weights[i_iLeft] * (double)(gdTrueColorGetAlpha(pCurSrc))); + } + pRes->tpixels[y][uCol] = gdTrueColorAlpha(r, g, b, a); + } +} + +static inline _gdScaleVert (const gdImagePtr pSrc, const unsigned int src_width, const unsigned int src_height, const gdImagePtr pDst, const unsigned int dst_width, const unsigned int dst_height) +{ + unsigned int u; + LineContribType * contrib; + + /* same height, copy it */ + if (src_height == dst_height) { + unsigned int y; + for (y = 0; y < src_height - 1; ++y) { + memcpy(pDst->tpixels[y], pSrc->tpixels[y], src_width); + } + } + + contrib = _gdContributionsCalc(dst_height, src_height, (double)(dst_height) / (double)(src_height), pSrc->interpolation); + /* scale each column */ + for (u = 0; u < dst_width - 1; u++) { + _gdScaleCol(pSrc, src_width, pDst, dst_width, dst_height, u, contrib); + } + _gdContributionsFree(contrib); +} + +gdImagePtr gdImageScaleTwoPass(const gdImagePtr src, const unsigned int src_width, const unsigned int src_height, const unsigned int new_width, const unsigned int new_height) +{ + gdImagePtr tmp_im; + gdImagePtr dst; + + tmp_im = gdImageCreateTrueColor(new_width, src_height); + if (tmp_im == NULL) { + return NULL; + } + _gdScaleHoriz (src, src_width, src_height, tmp_im, new_width, src_height); + + dst = gdImageCreateTrueColor(new_width, new_height); + if (dst == NULL) { + return NULL; + } + _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height); + gdFree(tmp_im); + + + return dst; +} + +gdImagePtr Scale(const gdImagePtr src, const unsigned int src_width, const unsigned int src_height, const gdImagePtr dst, const unsigned int new_width, const unsigned int new_height) +{ + gdImagePtr tmp_im; + + tmp_im = gdImageCreateTrueColor(new_width, src_height); + if (tmp_im == NULL) { + return NULL; + } + _gdScaleHoriz(src, src_width, src_height, tmp_im, new_width, src_height); + + _gdScaleVert(tmp_im, new_width, src_height, dst, new_width, new_height); + + gdFree(tmp_im); + return dst; +} + +/* + BilinearFixed, BicubicFixed and nearest implementations are rewamped versions of the implementation in CBitmapEx + http://www.codeproject.com/Articles/29121/CBitmapEx-Free-C-Bitmap-Manipulation-Class + Integer only implementation, good to have for common usages like pre scale very large + images before using another interpolation methods for the last step. +*/ +gdImagePtr gdImageScaleNearestNeighbour(gdImagePtr im, const unsigned int width, const unsigned int height) +{ + const unsigned long new_width = MAX(1, width); + const unsigned long new_height = MAX(1, height); + const float dx = (float)im->sx / (float)new_width; + const float dy = (float)im->sy / (float)new_height; + const gdFixed f_dx = gd_ftofx(dx); + const gdFixed f_dy = gd_ftofx(dy); + + gdImagePtr dst_img; + unsigned long dst_offset_x; + unsigned long dst_offset_y = 0; + unsigned int i; + + dst_img = gdImageCreateTrueColor(new_width, new_height); + + if (dst_img == NULL) { + return NULL; + } + + for (i=0; itrueColor) { + for (j=0; jtpixels[dst_offset_y][dst_offset_x++] = im->tpixels[m][n]; + } + } else { + for (j=0; jtpixels[dst_offset_y][dst_offset_x++] = colorIndex2RGBA(im->pixels[m][n]); + } + } + dst_offset_y++; + } + return dst_img; +} + +static inline int getPixelOverflowColorTC(gdImagePtr im, const int x, const int y, const int color) +{ + if (gdImageBoundsSafe(im, x, y)) { + const int c = im->tpixels[y][x]; + if (c == im->transparent) { + return gdTrueColorAlpha(0, 0, 0, 127); + } + return c; + } else { + register int border; + if (y < im->cy1) { + border = im->tpixels[0][im->cx1]; + goto processborder; + } + + if (y < im->cy1) { + border = im->tpixels[0][im->cx1]; + goto processborder; + } + + if (y > im->cy2) { + if (x >= im->cx1 && x <= im->cx1) { + border = im->tpixels[im->cy2][x]; + goto processborder; + } else { + return gdTrueColorAlpha(0, 0, 0, 127); + } + } + + /* y is bound safe at this point */ + if (x < im->cx1) { + border = im->tpixels[y][im->cx1]; + goto processborder; + } + + if (x > im->cx2) { + border = im->tpixels[y][im->cx2]; + } + +processborder: + if (border == im->transparent) { + return gdTrueColorAlpha(0, 0, 0, 127); + } else{ + return gdTrueColorAlpha(gdTrueColorGetRed(border), gdTrueColorGetGreen(border), gdTrueColorGetBlue(border), 127); + } + } +} + +static gdImagePtr gdImageScaleBilinearPalette(gdImagePtr im, const unsigned int new_width, const unsigned int new_height) +{ + long _width = MAX(1, new_width); + long _height = MAX(1, new_height); + float dx = (float)gdImageSX(im) / (float)_width; + float dy = (float)gdImageSY(im) / (float)_height; + gdFixed f_dx = gd_ftofx(dx); + gdFixed f_dy = gd_ftofx(dy); + gdFixed f_1 = gd_itofx(1); + + int dst_offset_h; + int dst_offset_v = 0; + long i; + gdImagePtr new_img; + const int transparent = im->transparent; + + new_img = gdImageCreateTrueColor(new_width, new_height); + if (new_img == NULL) { + return NULL; + } + new_img->transparent = gdTrueColorAlpha(im->red[transparent], im->green[transparent], im->blue[transparent], im->alpha[transparent]); + + for (i=0; i < _height; i++) { + long j; + const gdFixed f_i = gd_itofx(i); + const gdFixed f_a = gd_mulfx(f_i, f_dy); + register long m = gd_fxtoi(f_a); + + dst_offset_h = 0; + + for (j=0; j < _width; j++) { + /* Update bitmap */ + gdFixed f_j = gd_itofx(j); + gdFixed f_b = gd_mulfx(f_j, f_dx); + + const long n = gd_fxtoi(f_b); + gdFixed f_f = f_a - gd_itofx(m); + gdFixed f_g = f_b - gd_itofx(n); + + const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g); + const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g); + const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g); + const gdFixed f_w4 = gd_mulfx(f_f, f_g); + unsigned int pixel1; + unsigned int pixel2; + unsigned int pixel3; + unsigned int pixel4; + register gdFixed f_r1, f_r2, f_r3, f_r4, + f_g1, f_g2, f_g3, f_g4, + f_b1, f_b2, f_b3, f_b4, + f_a1, f_a2, f_a3, f_a4; + + /* zero for the background color, nothig gets outside anyway */ + pixel1 = getPixelOverflowPalette(im, n, m, 0); + pixel2 = getPixelOverflowPalette(im, n + 1, m, 0); + pixel3 = getPixelOverflowPalette(im, n, m + 1, 0); + pixel4 = getPixelOverflowPalette(im, n + 1, m + 1, 0); + + f_r1 = gd_itofx(gdTrueColorGetRed(pixel1)); + f_r2 = gd_itofx(gdTrueColorGetRed(pixel2)); + f_r3 = gd_itofx(gdTrueColorGetRed(pixel3)); + f_r4 = gd_itofx(gdTrueColorGetRed(pixel4)); + f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1)); + f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2)); + f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3)); + f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4)); + f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1)); + f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2)); + f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3)); + f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4)); + f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1)); + f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2)); + f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3)); + f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4)); + + { + const char red = (char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4)); + const char green = (char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4)); + const char blue = (char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4)); + const char alpha = (char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4)); + + new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha); + } + + dst_offset_h++; + } + + dst_offset_v++; + } + return new_img; +} + +static gdImagePtr gdImageScaleBilinearTC(gdImagePtr im, const unsigned int new_width, const unsigned int new_height) +{ + long dst_w = MAX(1, new_width); + long dst_h = MAX(1, new_height); + float dx = (float)gdImageSX(im) / (float)dst_w; + float dy = (float)gdImageSY(im) / (float)dst_h; + gdFixed f_dx = gd_ftofx(dx); + gdFixed f_dy = gd_ftofx(dy); + gdFixed f_1 = gd_itofx(1); + + int dst_offset_h; + int dst_offset_v = 0; + int dwSrcTotalOffset; + long i; + gdImagePtr new_img; + + new_img = gdImageCreateTrueColor(new_width, new_height); + if (!new_img){ + return NULL; + } + + for (i=0; i < dst_h; i++) { + long j; + dst_offset_h = 0; + for (j=0; j < dst_w; j++) { + /* Update bitmap */ + gdFixed f_i = gd_itofx(i); + gdFixed f_j = gd_itofx(j); + gdFixed f_a = gd_mulfx(f_i, f_dy); + gdFixed f_b = gd_mulfx(f_j, f_dx); + const long m = gd_fxtoi(f_a); + const long n = gd_fxtoi(f_b); + gdFixed f_f = f_a - gd_itofx(m); + gdFixed f_g = f_b - gd_itofx(n); + + const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g); + const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g); + const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g); + const gdFixed f_w4 = gd_mulfx(f_f, f_g); + unsigned int pixel1; + unsigned int pixel2; + unsigned int pixel3; + unsigned int pixel4; + register gdFixed f_r1, f_r2, f_r3, f_r4, + f_g1, f_g2, f_g3, f_g4, + f_b1, f_b2, f_b3, f_b4, + f_a1, f_a2, f_a3, f_a4; + dwSrcTotalOffset = m + n; + /* 0 for bgColor, nothing gets outside anyway */ + pixel1 = getPixelOverflowTC(im, n, m, 0); + pixel2 = getPixelOverflowTC(im, n + 1, m, 0); + pixel3 = getPixelOverflowTC(im, n, m + 1, 0); + pixel4 = getPixelOverflowTC(im, n + 1, m + 1, 0); + + f_r1 = gd_itofx(gdTrueColorGetRed(pixel1)); + f_r2 = gd_itofx(gdTrueColorGetRed(pixel2)); + f_r3 = gd_itofx(gdTrueColorGetRed(pixel3)); + f_r4 = gd_itofx(gdTrueColorGetRed(pixel4)); + f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1)); + f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2)); + f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3)); + f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4)); + f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1)); + f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2)); + f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3)); + f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4)); + f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1)); + f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2)); + f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3)); + f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4)); + { + const char red = (char) gd_fxtoi(gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4)); + const char green = (char) gd_fxtoi(gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4)); + const char blue = (char) gd_fxtoi(gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4)); + const char alpha = (char) gd_fxtoi(gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4)); + + new_img->tpixels[dst_offset_v][dst_offset_h] = gdTrueColorAlpha(red, green, blue, alpha); + } + + dst_offset_h++; + } + + dst_offset_v++; + } + return new_img; +} + +gdImagePtr gdImageScaleBilinear(gdImagePtr im, const unsigned int new_width, const unsigned int new_height) +{ + if (im->trueColor) { + return gdImageScaleBilinearTC(im, new_width, new_height); + } else { + return gdImageScaleBilinearPalette(im, new_width, new_height); + } +} + +gdImagePtr gdImageScaleBicubicFixed(gdImagePtr src, const unsigned int width, const unsigned int height) +{ + const long new_width = MAX(1, width); + const long new_height = MAX(1, height); + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const gdFixed f_dx = gd_ftofx((float)src_w / (float)new_width); + const gdFixed f_dy = gd_ftofx((float)src_h / (float)new_height); + const gdFixed f_1 = gd_itofx(1); + const gdFixed f_2 = gd_itofx(2); + const gdFixed f_4 = gd_itofx(4); + const gdFixed f_6 = gd_itofx(6); + const gdFixed f_gamma = gd_ftofx(1.04f); + gdImagePtr dst; + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + long i; + + /* impact perf a bit, but not that much. Implementation for palette + images can be done at a later point. + */ + if (src->trueColor == 0) { + gdImagePaletteToTrueColor(src); + } + + dst = gdImageCreateTrueColor(new_width, new_height); + if (!dst) { + return NULL; + } + + dst->saveAlphaFlag = 1; + + for (i=0; i < new_height; i++) { + long j; + dst_offset_x = 0; + + for (j=0; j < new_width; j++) { + const gdFixed f_a = gd_mulfx(gd_itofx(i), f_dy); + const gdFixed f_b = gd_mulfx(gd_itofx(j), f_dx); + const long m = gd_fxtoi(f_a); + const long n = gd_fxtoi(f_b); + const gdFixed f_f = f_a - gd_itofx(m); + const gdFixed f_g = f_b - gd_itofx(n); + unsigned int src_offset_x[16], src_offset_y[16]; + long k; + register gdFixed f_red = 0, f_green = 0, f_blue = 0, f_alpha = 0; + unsigned char red, green, blue, alpha = 0; + int *dst_row = dst->tpixels[dst_offset_y]; + + if ((m < 1) || (n < 1)) { + src_offset_x[0] = n; + src_offset_y[0] = m; + } else { + src_offset_x[0] = n - 1; + src_offset_y[0] = m; + } + + if (m < 1) { + src_offset_x[1] = n; + src_offset_y[1] = m; + } else { + src_offset_x[1] = n; + src_offset_y[1] = m; + } + + if ((m < 1) || (n >= src_w - 1)) { + src_offset_x[2] = n; + src_offset_y[2] = m; + } else { + src_offset_x[2] = n + 1; + src_offset_y[2] = m; + } + + if ((m < 1) || (n >= src_w - 2)) { + src_offset_x[3] = n; + src_offset_y[3] = m; + } else { + src_offset_x[3] = n + 1 + 1; + src_offset_y[3] = m; + } + + if (n < 1) { + src_offset_x[4] = n; + src_offset_y[4] = m; + } else { + src_offset_x[4] = n - 1; + src_offset_y[4] = m; + } + + src_offset_x[5] = n; + src_offset_y[5] = m; + if (n >= src_w-1) { + src_offset_x[6] = n; + src_offset_y[6] = m; + } else { + src_offset_x[6] = n + 1; + src_offset_y[6] = m; + } + + if (n >= src_w - 2) { + src_offset_x[7] = n; + src_offset_y[7] = m; + } else { + src_offset_x[7] = n + 1 + 1; + src_offset_y[7] = m; + } + + if ((m >= src_h - 1) || (n < 1)) { + src_offset_x[8] = n; + src_offset_y[8] = m; + } else { + src_offset_x[8] = n - 1; + src_offset_y[8] = m; + } + + if (m >= src_h - 1) { + src_offset_x[8] = n; + src_offset_y[8] = m; + } else { + src_offset_x[9] = n; + src_offset_y[9] = m; + } + + if ((m >= src_h-1) || (n >= src_w-1)) { + src_offset_x[10] = n; + src_offset_y[10] = m; + } else { + src_offset_x[10] = n + 1; + src_offset_y[10] = m; + } + + if ((m >= src_h - 1) || (n >= src_w - 2)) { + src_offset_x[11] = n; + src_offset_y[11] = m; + } else { + src_offset_x[11] = n + 1 + 1; + src_offset_y[11] = m; + } + + if ((m >= src_h - 2) || (n < 1)) { + src_offset_x[12] = n; + src_offset_y[12] = m; + } else { + src_offset_x[12] = n - 1; + src_offset_y[12] = m; + } + + if (m >= src_h - 2) { + src_offset_x[13] = n; + src_offset_y[13] = m; + } else { + src_offset_x[13] = n; + src_offset_y[13] = m; + } + + if ((m >= src_h - 2) || (n >= src_w - 1)) { + src_offset_x[14] = n; + src_offset_y[14] = m; + } else { + src_offset_x[14] = n + 1; + src_offset_y[14] = m; + } + + if ((m >= src_h - 2) || (n >= src_w - 2)) { + src_offset_x[15] = n; + src_offset_y[15] = m; + } else { + src_offset_x[15] = n + 1 + 1; + src_offset_y[15] = m; + } + + for (k = -1; k < 3; k++) { + const gdFixed f = gd_itofx(k)-f_f; + const gdFixed f_fm1 = f - f_1; + const gdFixed f_fp1 = f + f_1; + const gdFixed f_fp2 = f + f_2; + register gdFixed f_a = 0, f_b = 0, f_d = 0, f_c = 0; + register gdFixed f_RY; + int l; + + if (f_fp2 > 0) f_a = gd_mulfx(f_fp2, gd_mulfx(f_fp2,f_fp2)); + if (f_fp1 > 0) f_b = gd_mulfx(f_fp1, gd_mulfx(f_fp1,f_fp1)); + if (f > 0) f_c = gd_mulfx(f, gd_mulfx(f,f)); + if (f_fm1 > 0) f_d = gd_mulfx(f_fm1, gd_mulfx(f_fm1,f_fm1)); + + f_RY = gd_divfx((f_a - gd_mulfx(f_4,f_b) + gd_mulfx(f_6,f_c) - gd_mulfx(f_4,f_d)),f_6); + + for (l = -1; l < 3; l++) { + const gdFixed f = gd_itofx(l) - f_g; + const gdFixed f_fm1 = f - f_1; + const gdFixed f_fp1 = f + f_1; + const gdFixed f_fp2 = f + f_2; + register gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0; + register gdFixed f_RX, f_R, f_rs, f_gs, f_bs, f_ba; + register int c; + const int _k = ((k+1)*4) + (l+1); + + if (f_fp2 > 0) f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2)); + + if (f_fp1 > 0) f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1)); + + if (f > 0) f_c = gd_mulfx(f,gd_mulfx(f,f)); + + if (f_fm1 > 0) f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1)); + + f_RX = gd_divfx((f_a-gd_mulfx(f_4,f_b)+gd_mulfx(f_6,f_c)-gd_mulfx(f_4,f_d)),f_6); + f_R = gd_mulfx(f_RY,f_RX); + + c = src->tpixels[*(src_offset_y + _k)][*(src_offset_x + _k)]; + f_rs = gd_itofx(gdTrueColorGetRed(c)); + f_gs = gd_itofx(gdTrueColorGetGreen(c)); + f_bs = gd_itofx(gdTrueColorGetBlue(c)); + f_ba = gd_itofx(gdTrueColorGetAlpha(c)); + + f_red += gd_mulfx(f_rs,f_R); + f_green += gd_mulfx(f_gs,f_R); + f_blue += gd_mulfx(f_bs,f_R); + f_alpha += gd_mulfx(f_ba,f_R); + } + } + + red = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gamma)), 0, 255); + green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gamma)), 0, 255); + blue = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gamma)), 0, 255); + alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gamma)), 0, 127); + + *(dst_row + dst_offset_x) = gdTrueColorAlpha(red, green, blue, alpha); + + dst_offset_x++; + } + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, const unsigned int new_height) +{ + gdImagePtr im_scaled = NULL; + + if (src == NULL || src->interpolation_id < 0 || src->interpolation_id > GD_METHOD_COUNT) { + return 0; + } + + switch (src->interpolation_id) { + /*Special cases, optimized implementations */ + case GD_NEAREST_NEIGHBOUR: + im_scaled = gdImageScaleNearestNeighbour(src, new_width, new_height); + break; + + case GD_BILINEAR_FIXED: + im_scaled = gdImageScaleBilinear(src, new_width, new_height); + break; + + case GD_BICUBIC_FIXED: + im_scaled = gdImageScaleBicubicFixed(src, new_width, new_height); + break; + + /* generic */ + default: + if (src->interpolation == NULL) { + return NULL; + } + im_scaled = gdImageScaleTwoPass(src, src->sx, src->sy, new_width, new_height); + break; + } + return im_scaled; +} + +gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor) +{ + float _angle = ((float) (-degrees / 180.0f) * (float)M_PI); + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f); + const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f); + const gdFixed f_0_5 = gd_ftofx(0.5f); + const gdFixed f_H = gd_itofx(src_h/2); + const gdFixed f_W = gd_itofx(src_w/2); + const gdFixed f_cos = gd_ftofx(cos(-_angle)); + const gdFixed f_sin = gd_ftofx(sin(-_angle)); + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int i; + gdImagePtr dst; + + /* impact perf a bit, but not that much. Implementation for palette + images can be done at a later point. + */ + if (src->trueColor == 0) { + gdImagePaletteToTrueColor(src); + } + + dst = gdImageCreateTrueColor(new_width, new_height); + if (!dst) { + return NULL; + } + dst->saveAlphaFlag = 1; + for (i = 0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + for (j = 0; j < new_width; j++) { + gdFixed f_i = gd_itofx(i - new_height/2); + gdFixed f_j = gd_itofx(j-new_width/2); + gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; + gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; + long m = gd_fxtoi(f_m); + long n = gd_fxtoi(f_n); + + if ((m > 0) && (m < src_h-1) && (n > 0) && (n < src_w-1)) { + if (dst_offset_y < new_height) { + dst->tpixels[dst_offset_y][dst_offset_x++] = src->tpixels[m][n]; + } + } else { + if (dst_offset_y < new_height) { + dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; + } + } + } + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int bgColor) +{ + float _angle = ((float) (-degrees / 180.0f) * (float)M_PI); + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f); + const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f); + const gdFixed f_0_5 = gd_ftofx(0.5f); + const gdFixed f_H = gd_itofx(src_h/2); + const gdFixed f_W = gd_itofx(src_w/2); + const gdFixed f_cos = gd_ftofx(cos(-_angle)); + const gdFixed f_sin = gd_ftofx(sin(-_angle)); + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int i; + gdImagePtr dst; + + const gdFixed f_slop_y = f_sin; + const gdFixed f_slop_x = f_cos; + const gdFixed f_slop = f_slop_x > 0 && f_slop_x > 0 ? + f_slop_x > f_slop_y ? gd_divfx(f_slop_y, f_slop_x) : gd_divfx(f_slop_x, f_slop_y) + : 0; + + /* impact perf a bit, but not that much. Implementation for palette + images can be done at a later point. + */ + if (src->trueColor == 0) { + gdImagePaletteToTrueColor(src); + } + + dst = gdImageCreateTrueColor(new_width, new_height); + if (!dst) { + return NULL; + } + dst->saveAlphaFlag = 1; + + for (i = 0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + for (j = 0; j < new_width; j++) { + gdFixed f_i = gd_itofx(i - new_height/ 2); + gdFixed f_j = gd_itofx(j -new_width / 2); + gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; + gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; + long m = gd_fxtoi(f_m); + long n = gd_fxtoi(f_n); + + if ((n <= 0) || (m <= 0) || (m >= src_h) || (n >= src_w)) { + dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; + } else if ((n <= 1) || (m <= 1) || (m >= src_h - 1) || (n >= src_w - 1)) { + gdFixed f_127 = gd_itofx(127); + register int c = getPixelInterpolated(src, n, m, bgColor); + c = c | (( gdTrueColorGetAlpha(c) + ((int)(127* gd_fxtof(f_slop)))) << 24); + + dst->tpixels[dst_offset_y][dst_offset_x++] = _color_blend(bgColor, c); + } else { + dst->tpixels[dst_offset_y][dst_offset_x++] = getPixelInterpolated(src, n, m, bgColor); + } + } + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int bgColor) +{ + float _angle = (float)((- degrees / 180.0f) * M_PI); + const unsigned int src_w = gdImageSX(src); + const unsigned int src_h = gdImageSY(src); + unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f)); + unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f)); + const gdFixed f_0_5 = gd_ftofx(0.5f); + const gdFixed f_H = gd_itofx(src_h/2); + const gdFixed f_W = gd_itofx(src_w/2); + const gdFixed f_cos = gd_ftofx(cos(-_angle)); + const gdFixed f_sin = gd_ftofx(sin(-_angle)); + const gdFixed f_1 = gd_itofx(1); + unsigned int i; + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int src_offset_x, src_offset_y; + gdImagePtr dst; + + /* impact perf a bit, but not that much. Implementation for palette + images can be done at a later point. + */ + if (src->trueColor == 0) { + gdImagePaletteToTrueColor(src); + } + + dst = gdImageCreateTrueColor(new_width, new_height); + if (dst == NULL) { + return NULL; + } + dst->saveAlphaFlag = 1; + + for (i = 0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + + for (j=0; j < new_width; j++) { + const gdFixed f_i = gd_itofx(i-new_height/2); + const gdFixed f_j = gd_itofx(j-new_width/2); + const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; + const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; + const unsigned int m = gd_fxtoi(f_m); + const unsigned int n = gd_fxtoi(f_n); + + if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w - 1)) { + const gdFixed f_f = f_m - gd_itofx(m); + const gdFixed f_g = f_n - gd_itofx(n); + const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g); + const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g); + const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g); + const gdFixed f_w4 = gd_mulfx(f_f, f_g); + + if (n < src_w - 1) { + src_offset_x = m + 1; + src_offset_y = n; + } + + if (m < src_h-1) { + src_offset_x = m; + src_offset_y = n + 1; + } + + if (!((n >= src_w-1) || (m >= src_h-1))) { + src_offset_x = m + 1; + src_offset_y = n + 1; + } + { + const int pixel1 = src->tpixels[src_offset_y][src_offset_x]; + register int pixel2, pixel3, pixel4; + + if (src_offset_y + 1 >= src_h) { + pixel2 = bgColor; + pixel3 = bgColor; + pixel4 = bgColor; + } else if (src_offset_x + 1 >= src_w) { + pixel2 = bgColor; + pixel3 = bgColor; + pixel4 = bgColor; + } else { + pixel2 = src->tpixels[src_offset_y][src_offset_x + 1]; + pixel3 = src->tpixels[src_offset_y + 1][src_offset_x]; + pixel4 = src->tpixels[src_offset_y + 1][src_offset_x + 1]; + } + { + const gdFixed f_r1 = gd_itofx(gdTrueColorGetRed(pixel1)); + const gdFixed f_r2 = gd_itofx(gdTrueColorGetRed(pixel2)); + const gdFixed f_r3 = gd_itofx(gdTrueColorGetRed(pixel3)); + const gdFixed f_r4 = gd_itofx(gdTrueColorGetRed(pixel4)); + const gdFixed f_g1 = gd_itofx(gdTrueColorGetGreen(pixel1)); + const gdFixed f_g2 = gd_itofx(gdTrueColorGetGreen(pixel2)); + const gdFixed f_g3 = gd_itofx(gdTrueColorGetGreen(pixel3)); + const gdFixed f_g4 = gd_itofx(gdTrueColorGetGreen(pixel4)); + const gdFixed f_b1 = gd_itofx(gdTrueColorGetBlue(pixel1)); + const gdFixed f_b2 = gd_itofx(gdTrueColorGetBlue(pixel2)); + const gdFixed f_b3 = gd_itofx(gdTrueColorGetBlue(pixel3)); + const gdFixed f_b4 = gd_itofx(gdTrueColorGetBlue(pixel4)); + const gdFixed f_a1 = gd_itofx(gdTrueColorGetAlpha(pixel1)); + const gdFixed f_a2 = gd_itofx(gdTrueColorGetAlpha(pixel2)); + const gdFixed f_a3 = gd_itofx(gdTrueColorGetAlpha(pixel3)); + const gdFixed f_a4 = gd_itofx(gdTrueColorGetAlpha(pixel4)); + const gdFixed f_red = gd_mulfx(f_w1, f_r1) + gd_mulfx(f_w2, f_r2) + gd_mulfx(f_w3, f_r3) + gd_mulfx(f_w4, f_r4); + const gdFixed f_green = gd_mulfx(f_w1, f_g1) + gd_mulfx(f_w2, f_g2) + gd_mulfx(f_w3, f_g3) + gd_mulfx(f_w4, f_g4); + const gdFixed f_blue = gd_mulfx(f_w1, f_b1) + gd_mulfx(f_w2, f_b2) + gd_mulfx(f_w3, f_b3) + gd_mulfx(f_w4, f_b4); + const gdFixed f_alpha = gd_mulfx(f_w1, f_a1) + gd_mulfx(f_w2, f_a2) + gd_mulfx(f_w3, f_a3) + gd_mulfx(f_w4, f_a4); + + const unsigned char red = (unsigned char) CLAMP(gd_fxtoi(f_red), 0, 255); + const unsigned char green = (unsigned char) CLAMP(gd_fxtoi(f_green), 0, 255); + const unsigned char blue = (unsigned char) CLAMP(gd_fxtoi(f_blue), 0, 255); + const unsigned char alpha = (unsigned char) CLAMP(gd_fxtoi(f_alpha), 0, 127); + + dst->tpixels[dst_offset_y][dst_offset_x++] = gdTrueColorAlpha(red, green, blue, alpha); + } + } + } else { + dst->tpixels[dst_offset_y][dst_offset_x++] = bgColor; + } + } + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const int bgColor) +{ + const float _angle = (float)((- degrees / 180.0f) * M_PI); + const int src_w = gdImageSX(src); + const int src_h = gdImageSY(src); + const unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f)); + const unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f)); + const gdFixed f_0_5 = gd_ftofx(0.5f); + const gdFixed f_H = gd_itofx(src_h/2); + const gdFixed f_W = gd_itofx(src_w/2); + const gdFixed f_cos = gd_ftofx(cos(-_angle)); + const gdFixed f_sin = gd_ftofx(sin(-_angle)); + const gdFixed f_1 = gd_itofx(1); + const gdFixed f_2 = gd_itofx(2); + const gdFixed f_4 = gd_itofx(4); + const gdFixed f_6 = gd_itofx(6); + const gdFixed f_gama = gd_ftofx(1.04f); + + unsigned int dst_offset_x; + unsigned int dst_offset_y = 0; + unsigned int i; + gdImagePtr dst; + + /* impact perf a bit, but not that much. Implementation for palette + images can be done at a later point. + */ + if (src->trueColor == 0) { + gdImagePaletteToTrueColor(src); + } + + dst = gdImageCreateTrueColor(new_width, new_height); + + if (dst == NULL) { + return NULL; + } + dst->saveAlphaFlag = 1; + + for (i=0; i < new_height; i++) { + unsigned int j; + dst_offset_x = 0; + + for (j=0; j < new_width; j++) { + const gdFixed f_i = gd_itofx(i-new_height/2); + const gdFixed f_j = gd_itofx(j-new_width/2); + const gdFixed f_m = gd_mulfx(f_j,f_sin) + gd_mulfx(f_i,f_cos) + f_0_5 + f_H; + const gdFixed f_n = gd_mulfx(f_j,f_cos) - gd_mulfx(f_i,f_sin) + f_0_5 + f_W; + const int m = gd_fxtoi(f_m); + const int n = gd_fxtoi(f_n); + + if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w-1)) { + const gdFixed f_f = f_m - gd_itofx(m); + const gdFixed f_g = f_n - gd_itofx(n); + unsigned int src_offset_x[16], src_offset_y[16]; + unsigned char red, green, blue, alpha; + gdFixed f_red=0, f_green=0, f_blue=0, f_alpha=0; + int k; + + if ((m < 1) || (n < 1)) { + src_offset_x[0] = n; + src_offset_y[0] = m; + } else { + src_offset_x[0] = n - 1; + src_offset_y[0] = m; + } + + if (m < 1) { + src_offset_x[1] = n; + src_offset_y[1] = m; + } else { + src_offset_x[1] = n; + src_offset_y[1] = m ; + } + + if ((m < 1) || (n >= src_w-1)) { + src_offset_x[2] = - 1; + src_offset_y[2] = - 1; + } else { + src_offset_x[2] = n + 1; + src_offset_y[2] = m ; + } + + if ((m < 1) || (n >= src_w-2)) { + src_offset_x[3] = - 1; + src_offset_y[3] = - 1; + } else { + src_offset_x[3] = n + 1 + 1; + src_offset_y[3] = m ; + } + + if (n < 1) { + src_offset_x[4] = - 1; + src_offset_y[4] = - 1; + } else { + src_offset_x[4] = n - 1; + src_offset_y[4] = m; + } + + src_offset_x[5] = n; + src_offset_y[5] = m; + if (n >= src_w-1) { + src_offset_x[6] = - 1; + src_offset_y[6] = - 1; + } else { + src_offset_x[6] = n + 1; + src_offset_y[6] = m; + } + + if (n >= src_w-2) { + src_offset_x[7] = - 1; + src_offset_y[7] = - 1; + } else { + src_offset_x[7] = n + 1 + 1; + src_offset_y[7] = m; + } + + if ((m >= src_h-1) || (n < 1)) { + src_offset_x[8] = - 1; + src_offset_y[8] = - 1; + } else { + src_offset_x[8] = n - 1; + src_offset_y[8] = m; + } + + if (m >= src_h-1) { + src_offset_x[8] = - 1; + src_offset_y[8] = - 1; + } else { + src_offset_x[9] = n; + src_offset_y[9] = m; + } + + if ((m >= src_h-1) || (n >= src_w-1)) { + src_offset_x[10] = - 1; + src_offset_y[10] = - 1; + } else { + src_offset_x[10] = n + 1; + src_offset_y[10] = m; + } + + if ((m >= src_h-1) || (n >= src_w-2)) { + src_offset_x[11] = - 1; + src_offset_y[11] = - 1; + } else { + src_offset_x[11] = n + 1 + 1; + src_offset_y[11] = m; + } + + if ((m >= src_h-2) || (n < 1)) { + src_offset_x[12] = - 1; + src_offset_y[12] = - 1; + } else { + src_offset_x[12] = n - 1; + src_offset_y[12] = m; + } + + if (m >= src_h-2) { + src_offset_x[13] = - 1; + src_offset_y[13] = - 1; + } else { + src_offset_x[13] = n; + src_offset_y[13] = m; + } + + if ((m >= src_h-2) || (n >= src_w - 1)) { + src_offset_x[14] = - 1; + src_offset_y[14] = - 1; + } else { + src_offset_x[14] = n + 1; + src_offset_y[14] = m; + } + + if ((m >= src_h-2) || (n >= src_w-2)) { + src_offset_x[15] = - 1; + src_offset_y[15] = - 1; + } else { + src_offset_x[15] = n + 1 + 1; + src_offset_y[15] = m; + } + + for (k=-1; k<3; k++) { + const gdFixed f = gd_itofx(k)-f_f; + const gdFixed f_fm1 = f - f_1; + const gdFixed f_fp1 = f + f_1; + const gdFixed f_fp2 = f + f_2; + gdFixed f_a = 0, f_b = 0,f_c = 0, f_d = 0; + gdFixed f_RY; + int l; + + if (f_fp2 > 0) { + f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2)); + } + + if (f_fp1 > 0) { + f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1)); + } + + if (f > 0) { + f_c = gd_mulfx(f,gd_mulfx(f,f)); + } + + if (f_fm1 > 0) { + f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1)); + } + f_RY = gd_divfx((f_a-gd_mulfx(f_4,f_b)+gd_mulfx(f_6,f_c)-gd_mulfx(f_4,f_d)),f_6); + + for (l=-1; l< 3; l++) { + const gdFixed f = gd_itofx(l) - f_g; + const gdFixed f_fm1 = f - f_1; + const gdFixed f_fp1 = f + f_1; + const gdFixed f_fp2 = f + f_2; + gdFixed f_a = 0, f_b = 0, f_c = 0, f_d = 0; + gdFixed f_RX, f_R; + const int _k = ((k + 1) * 4) + (l + 1); + register gdFixed f_rs, f_gs, f_bs, f_as; + register int c; + + if (f_fp2 > 0) { + f_a = gd_mulfx(f_fp2,gd_mulfx(f_fp2,f_fp2)); + } + + if (f_fp1 > 0) { + f_b = gd_mulfx(f_fp1,gd_mulfx(f_fp1,f_fp1)); + } + + if (f > 0) { + f_c = gd_mulfx(f,gd_mulfx(f,f)); + } + + if (f_fm1 > 0) { + f_d = gd_mulfx(f_fm1,gd_mulfx(f_fm1,f_fm1)); + } + + f_RX = gd_divfx((f_a - gd_mulfx(f_4, f_b) + gd_mulfx(f_6, f_c) - gd_mulfx(f_4, f_d)), f_6); + f_R = gd_mulfx(f_RY, f_RX); + + if ((src_offset_x[_k] <= 0) || (src_offset_y[_k] <= 0) || (src_offset_y[_k] >= src_h) || (src_offset_x[_k] >= src_w)) { + c = bgColor; + } else if ((src_offset_x[_k] <= 1) || (src_offset_y[_k] <= 1) || (src_offset_y[_k] >= (int)src_h - 1) || (src_offset_x[_k] >= (int)src_w - 1)) { + gdFixed f_127 = gd_itofx(127); + c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]]; + c = c | (( (int) (gd_fxtof(gd_mulfx(f_R, f_127)) + 50.5f)) << 24); + c = _color_blend(bgColor, c); + } else { + c = src->tpixels[src_offset_y[_k]][src_offset_x[_k]]; + } + + f_rs = gd_itofx(gdTrueColorGetRed(c)); + f_gs = gd_itofx(gdTrueColorGetGreen(c)); + f_bs = gd_itofx(gdTrueColorGetBlue(c)); + f_as = gd_itofx(gdTrueColorGetAlpha(c)); + + f_red += gd_mulfx(f_rs, f_R); + f_green += gd_mulfx(f_gs, f_R); + f_blue += gd_mulfx(f_bs, f_R); + f_alpha += gd_mulfx(f_as, f_R); + } + } + + red = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_red, f_gama)), 0, 255); + green = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_green, f_gama)), 0, 255); + blue = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_blue, f_gama)), 0, 255); + alpha = (unsigned char) CLAMP(gd_fxtoi(gd_mulfx(f_alpha, f_gama)), 0, 127); + + dst->tpixels[dst_offset_y][dst_offset_x] = gdTrueColorAlpha(red, green, blue, alpha); + } else { + dst->tpixels[dst_offset_y][dst_offset_x] = bgColor; + } + dst_offset_x++; + } + + dst_offset_y++; + } + return dst; +} + +gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor) +{ + const int angle_rounded = (int)floor(angle * 100); + + if (bgcolor < 0) { + return NULL; + } + + /* no interpolation needed here */ + switch (angle_rounded) { + case 9000: + return gdImageRotate90(src, 0); + case 18000: + return gdImageRotate180(src, 0); + case 27000: + return gdImageRotate270(src, 0); + } + + if (src == NULL || src->interpolation_id < 1 || src->interpolation_id > GD_METHOD_COUNT) { + return NULL; + } + + switch (src->interpolation_id) { + case GD_NEAREST_NEIGHBOUR: + return gdImageRotateNearestNeighbour(src, angle, bgcolor); + break; + + case GD_BILINEAR_FIXED: + return gdImageRotateBilinear(src, angle, bgcolor); + break; + + case GD_BICUBIC_FIXED: + return gdImageRotateBicubicFixed(src, angle, bgcolor); + break; + + default: + return gdImageRotateGeneric(src, angle, bgcolor); + } + return NULL; +} + +/** + * Title: Affine transformation + **/ + +/** + * Group: Transform + **/ + + static void gdImageClipRectangle(gdImagePtr im, gdRectPtr r) +{ + int c1x, c1y, c2x, c2y; + int x1,y1; + + gdImageGetClip(im, &c1x, &c1y, &c2x, &c2y); + x1 = r->x + r->width - 1; + y1 = r->y + r->height - 1; + r->x = CLAMP(r->x, c1x, c2x); + r->y = CLAMP(r->y, c1y, c2y); + r->width = CLAMP(x1, c1x, c2x) - r->x + 1; + r->height = CLAMP(y1, c1y, c2y) - r->y + 1; +} + +void gdDumpRect(const char *msg, gdRectPtr r) +{ + printf("%s (%i, %i) (%i, %i)\n", msg, r->x, r->y, r->width, r->height); +} + +/** + * Function: gdTransformAffineGetImage + * Applies an affine transformation to a region and return an image + * containing the complete transformation. + * + * Parameters: + * dst - Pointer to a gdImagePtr to store the created image, NULL when + * the creation or the transformation failed + * src - Source image + * src_area - rectangle defining the source region to transform + * dstY - Y position in the destination image + * affine - The desired affine transformation + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdTransformAffineGetImage(gdImagePtr *dst, + const gdImagePtr src, + gdRectPtr src_area, + const double affine[6]) +{ + int res; + double m[6]; + gdRect bbox; + gdRect area_full; + + if (src_area == NULL) { + area_full.x = 0; + area_full.y = 0; + area_full.width = gdImageSX(src); + area_full.height = gdImageSY(src); + src_area = &area_full; + } + + gdTransformAffineBoundingBox(src_area, affine, &bbox); + + *dst = gdImageCreateTrueColor(bbox.width, bbox.height); + if (*dst == NULL) { + return GD_FALSE; + } + (*dst)->saveAlphaFlag = 1; + + if (!src->trueColor) { + gdImagePaletteToTrueColor(src); + } + + /* Translate to dst origin (0,0) */ + gdAffineTranslate(m, -bbox.x, -bbox.y); + gdAffineConcat(m, affine, m); + + gdImageAlphaBlending(*dst, 0); + + res = gdTransformAffineCopy(*dst, + 0,0, + src, + src_area, + m); + + if (res != GD_TRUE) { + gdImageDestroy(*dst); + dst = NULL; + return GD_FALSE; + } else { + return GD_TRUE; + } +} + +/** + * Function: gdTransformAffineCopy + * Applies an affine transformation to a region and copy the result + * in a destination to the given position. + * + * Parameters: + * dst - Image to draw the transformed image + * src - Source image + * dstX - X position in the destination image + * dstY - Y position in the destination image + * src_area - Rectangular region to rotate in the src image + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdTransformAffineCopy(gdImagePtr dst, + int dst_x, int dst_y, + const gdImagePtr src, + gdRectPtr src_region, + const double affine[6]) +{ + int c1x,c1y,c2x,c2y; + int backclip = 0; + int backup_clipx1, backup_clipy1, backup_clipx2, backup_clipy2; + register int x, y, src_offset_x, src_offset_y; + double inv[6]; + int *dst_p; + gdPointF pt, src_pt; + gdRect bbox; + int end_x, end_y; + gdInterpolationMethod interpolotion_id_bak; + interpolation_method interpolation_bak; + + /* These methods use special implementations */ + if (src->interpolation_id == GD_BILINEAR_FIXED || src->interpolation_id == GD_BICUBIC_FIXED || src->interpolation_id == GD_NEAREST_NEIGHBOUR) { + interpolotion_id_bak = src->interpolation_id; + interpolation_bak = src->interpolation; + + gdImageSetInterpolationMethod(src, GD_BICUBIC); + } + + + gdImageClipRectangle(src, src_region); + + if (src_region->x > 0 || src_region->y > 0 + || src_region->width < gdImageSX(src) + || src_region->height < gdImageSY(src)) { + backclip = 1; + + gdImageGetClip(src, &backup_clipx1, &backup_clipy1, + &backup_clipx2, &backup_clipy2); + + gdImageSetClip(src, src_region->x, src_region->y, + src_region->x + src_region->width - 1, + src_region->y + src_region->height - 1); + } + + if (!gdTransformAffineBoundingBox(src_region, affine, &bbox)) { + if (backclip) { + gdImageSetClip(src, backup_clipx1, backup_clipy1, + backup_clipx2, backup_clipy2); + } + gdImageSetInterpolationMethod(src, interpolotion_id_bak); + return GD_FALSE; + } + + gdImageGetClip(dst, &c1x, &c1y, &c2x, &c2y); + + end_x = bbox.width + (int) fabs(bbox.x); + end_y = bbox.height + (int) fabs(bbox.y); + + /* Get inverse affine to let us work with destination -> source */ + gdAffineInvert(inv, affine); + + src_offset_x = src_region->x; + src_offset_y = src_region->y; + + if (dst->alphaBlendingFlag) { + for (y = bbox.y; y <= end_y; y++) { + pt.y = y + 0.5; + for (x = 0; x <= end_x; x++) { + pt.x = x + 0.5; + gdAffineApplyToPointF(&src_pt, &pt, inv); + gdImageSetPixel(dst, dst_x + x, dst_y + y, getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, 0)); + } + } + } else { + for (y = 0; y <= end_y; y++) { + pt.y = y + 0.5 + bbox.y; + if ((dst_y + y) < 0 || ((dst_y + y) > gdImageSY(dst) -1)) { + continue; + } + dst_p = dst->tpixels[dst_y + y] + dst_x; + + for (x = 0; x <= end_x; x++) { + pt.x = x + 0.5 + bbox.x; + gdAffineApplyToPointF(&src_pt, &pt, inv); + + if ((dst_x + x) < 0 || (dst_x + x) > (gdImageSX(dst) - 1)) { + break; + } + *(dst_p++) = getPixelInterpolated(src, src_offset_x + src_pt.x, src_offset_y + src_pt.y, -1); + } + } + } + + /* Restore clip if required */ + if (backclip) { + gdImageSetClip(src, backup_clipx1, backup_clipy1, + backup_clipx2, backup_clipy2); + } + + gdImageSetInterpolationMethod(src, interpolotion_id_bak); + return GD_TRUE; +} + +/** + * Function: gdTransformAffineBoundingBox + * Returns the bounding box of an affine transformation applied to a + * rectangular area + * + * Parameters: + * src - Rectangular source area for the affine transformation + * affine - the affine transformation + * bbox - the resulting bounding box + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdTransformAffineBoundingBox(gdRectPtr src, const double affine[6], gdRectPtr bbox) +{ + gdPointF extent[4], min, max, point; + int i; + + extent[0].x=0.0; + extent[0].y=0.0; + extent[1].x=(double) src->width; + extent[1].y=0.0; + extent[2].x=(double) src->width; + extent[2].y=(double) src->height; + extent[3].x=0.0; + extent[3].y=(double) src->height; + + for (i=0; i < 4; i++) { + point=extent[i]; + if (gdAffineApplyToPointF(&extent[i], &point, affine) != GD_TRUE) { + return GD_FALSE; + } + } + min=extent[0]; + max=extent[0]; + + for (i=1; i < 4; i++) { + if (min.x > extent[i].x) + min.x=extent[i].x; + if (min.y > extent[i].y) + min.y=extent[i].y; + if (max.x < extent[i].x) + max.x=extent[i].x; + if (max.y < extent[i].y) + max.y=extent[i].y; + } + bbox->x = (int) min.x; + bbox->y = (int) min.y; + bbox->width = (int) floor(max.x - min.x) - 1; + bbox->height = (int) floor(max.y - min.y); + return GD_TRUE; +} + +int gdImageSetInterpolationMethod(gdImagePtr im, gdInterpolationMethod id) +{ + if (im == NULL || id < 0 || id > GD_METHOD_COUNT) { + return 0; + } + + switch (id) { + case GD_DEFAULT: + im->interpolation_id = GD_BILINEAR_FIXED; + im->interpolation = NULL; + break; + + /* Optimized versions */ + case GD_BILINEAR_FIXED: + case GD_BICUBIC_FIXED: + case GD_NEAREST_NEIGHBOUR: + case GD_WEIGHTED4: + im->interpolation = NULL; + break; + + /* generic versions*/ + case GD_BELL: + im->interpolation = filter_bell; + break; + case GD_BESSEL: + im->interpolation = filter_bessel; + break; + case GD_BICUBIC: + im->interpolation = filter_bicubic; + break; + case GD_BLACKMAN: + im->interpolation = filter_blackman; + break; + case GD_BOX: + im->interpolation = filter_box; + break; + case GD_BSPLINE: + im->interpolation = filter_bspline; + break; + case GD_CATMULLROM: + im->interpolation = filter_catmullrom; + break; + case GD_GAUSSIAN: + im->interpolation = filter_gaussian; + break; + case GD_GENERALIZED_CUBIC: + im->interpolation = filter_generalized_cubic; + break; + case GD_HERMITE: + im->interpolation = filter_hermite; + break; + case GD_HAMMING: + im->interpolation = filter_hamming; + break; + case GD_HANNING: + im->interpolation = filter_hanning; + break; + case GD_MITCHELL: + im->interpolation = filter_mitchell; + break; + case GD_POWER: + im->interpolation = filter_power; + break; + case GD_QUADRATIC: + im->interpolation = filter_quadratic; + break; + case GD_SINC: + im->interpolation = filter_sinc; + break; + case GD_TRIANGLE: + im->interpolation = filter_triangle; + break; + + default: + return 0; + break; + } + im->interpolation_id = id; + return 1; +} + +#ifdef _MSC_VER +# pragma optimize("", on) +#endif diff --git a/ext/gd/libgd/gd_matrix.c b/ext/gd/libgd/gd_matrix.c new file mode 100644 index 00000000000..83438bdbe31 --- /dev/null +++ b/ext/gd/libgd/gd_matrix.c @@ -0,0 +1,334 @@ +#include "gd.h" +#include + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +/** + * Title: Matrix + * Group: Affine Matrix + */ + +/** + * Function: gdAffineApplyToPointF + * Applies an affine transformation to a point (floating point + * gdPointF) + * + * + * Parameters: + * dst - Where to store the resulting point + * affine - Source Point + * flip_horz - affine matrix + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdAffineApplyToPointF (gdPointFPtr dst, const gdPointFPtr src, + const double affine[6]) +{ + double x = src->x; + double y = src->y; + x = src->x; + y = src->y; + dst->x = x * affine[0] + y * affine[2] + affine[4]; + dst->y = x * affine[1] + y * affine[3] + affine[5]; + return GD_TRUE; +} + +/** + * Function: gdAffineInvert + * Find the inverse of an affine transformation. + * + * All non-degenerate affine transforms are invertible. Applying the + * inverted matrix will restore the original values. Multiplying + * by (commutative) will return the identity affine (rounding + * error possible). + * + * Parameters: + * dst - Where to store the resulting affine transform + * src_affine - Original affine matrix + * flip_horz - Whether or not to flip horizontally + * flip_vert - Whether or not to flip vertically + * + * See also: + * + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdAffineInvert (double dst[6], const double src[6]) +{ + double r_det = (src[0] * src[3] - src[1] * src[2]); + + if (r_det <= 0.0) { + return GD_FALSE; + } + + r_det = 1.0 / r_det; + dst[0] = src[3] * r_det; + dst[1] = -src[1] * r_det; + dst[2] = -src[2] * r_det; + dst[3] = src[0] * r_det; + dst[4] = -src[4] * dst[0] - src[5] * dst[2]; + dst[5] = -src[4] * dst[1] - src[5] * dst[3]; + return GD_TRUE; +} + +/** + * Function: gdAffineFlip + * Flip an affine transformation horizontally or vertically. + * + * Flips the affine transform, giving GD_FALSE for and + * will clone the affine matrix. GD_TRUE for both will + * copy a 180° rotation. + * + * Parameters: + * dst - Where to store the resulting affine transform + * src_affine - Original affine matrix + * flip_h - Whether or not to flip horizontally + * flip_v - Whether or not to flip vertically + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineFlip (double dst[6], const double src[6], const int flip_h, const int flip_v) +{ + dst[0] = flip_h ? - src[0] : src[0]; + dst[1] = flip_h ? - src[1] : src[1]; + dst[2] = flip_v ? - src[2] : src[2]; + dst[3] = flip_v ? - src[3] : src[3]; + dst[4] = flip_h ? - src[4] : src[4]; + dst[5] = flip_v ? - src[5] : src[5]; + return GD_TRUE; +} + +/** + * Function: gdAffineConcat + * Concat (Multiply) two affine transformation matrices. + * + * Concats two affine transforms together, i.e. the result + * will be the equivalent of doing first the transformation m1 and then + * m2. All parameters can be the same matrix (safe to call using + * the same array for all three arguments). + * + * Parameters: + * dst - Where to store the resulting affine transform + * m1 - First affine matrix + * m2 - Second affine matrix + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineConcat (double dst[6], const double m1[6], const double m2[6]) +{ + double dst0, dst1, dst2, dst3, dst4, dst5; + + dst0 = m1[0] * m2[0] + m1[1] * m2[2]; + dst1 = m1[0] * m2[1] + m1[1] * m2[3]; + dst2 = m1[2] * m2[0] + m1[3] * m2[2]; + dst3 = m1[2] * m2[1] + m1[3] * m2[3]; + dst4 = m1[4] * m2[0] + m1[5] * m2[2] + m2[4]; + dst5 = m1[4] * m2[1] + m1[5] * m2[3] + m2[5]; + dst[0] = dst0; + dst[1] = dst1; + dst[2] = dst2; + dst[3] = dst3; + dst[4] = dst4; + dst[5] = dst5; + return GD_TRUE; +} + +/** + * Function: gdAffineIdentity + * Set up the identity matrix. + * + * Parameters: + * dst - Where to store the resulting affine transform + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineIdentity (double dst[6]) +{ + dst[0] = 1; + dst[1] = 0; + dst[2] = 0; + dst[3] = 1; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineScale + * Set up a scaling matrix. + * + * Parameters: + * scale_x - X scale factor + * scale_y - Y scale factor + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineScale (double dst[6], const double scale_x, const double scale_y) +{ + dst[0] = scale_x; + dst[1] = 0; + dst[2] = 0; + dst[3] = scale_y; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineRotate + * Set up a rotation affine transform. + * + * Like the other angle in libGD, in which increasing y moves + * downward, this is a counterclockwise rotation. + * + * Parameters: + * dst - Where to store the resulting affine transform + * angle - Rotation angle in degrees + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineRotate (double dst[6], const double angle) +{ + const double sin_t = sin (angle * M_PI / 180.0); + const double cos_t = cos (angle * M_PI / 180.0); + + dst[0] = cos_t; + dst[1] = sin_t; + dst[2] = -sin_t; + dst[3] = cos_t; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineShearHorizontal + * Set up a horizontal shearing matrix || becomes \\. + * + * Parameters: + * dst - Where to store the resulting affine transform + * angle - Shear angle in degrees + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineShearHorizontal(double dst[6], const double angle) +{ + dst[0] = 1; + dst[1] = 0; + dst[2] = tan(angle * M_PI / 180.0); + dst[3] = 1; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineShearVertical + * Set up a vertical shearing matrix, columns are untouched. + * + * Parameters: + * dst - Where to store the resulting affine transform + * angle - Shear angle in degrees + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineShearVertical(double dst[6], const double angle) +{ + dst[0] = 1; + dst[1] = tan(angle * M_PI / 180.0);; + dst[2] = 0; + dst[3] = 1; + dst[4] = 0; + dst[5] = 0; + return GD_TRUE; +} + +/** + * Function: gdAffineTranslate + * Set up a translation matrix. + * + * Parameters: + * dst - Where to store the resulting affine transform + * offset_x - Horizontal translation amount + * offset_y - Vertical translation amount + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineTranslate (double dst[6], const double offset_x, const double offset_y) +{ + dst[0] = 1; + dst[1] = 0; + dst[2] = 0; + dst[3] = 1; + dst[4] = offset_x; + dst[5] = offset_y; + return GD_TRUE; +} + +/** + * gdAffineexpansion: Find the affine's expansion factor. + * @src: The affine transformation. + * + * Finds the expansion factor, i.e. the square root of the factor + * by which the affine transform affects area. In an affine transform + * composed of scaling, rotation, shearing, and translation, returns + * the amount of scaling. + * + * GD_SUCCESS on success or GD_FAILURE + **/ +double gdAffineExpansion (const double src[6]) +{ + return sqrt (fabs (src[0] * src[3] - src[1] * src[2])); +} + +/** + * Function: gdAffineRectilinear + * Determines whether the affine transformation is axis aligned. A + * tolerance has been implemented using GD_EPSILON. + * + * Parameters: + * m - The affine transformation + * + * Returns: + * GD_TRUE if the affine is rectilinear or GD_FALSE + */ +int gdAffineRectilinear (const double m[6]) +{ + return ((fabs (m[1]) < GD_EPSILON && fabs (m[2]) < GD_EPSILON) || + (fabs (m[0]) < GD_EPSILON && fabs (m[3]) < GD_EPSILON)); +} + +/** + * Function: gdAffineEqual + * Determines whether two affine transformations are equal. A tolerance + * has been implemented using GD_EPSILON. + * + * Parameters: + * m1 - The first affine transformation + * m2 - The first affine transformation + * + * Returns: + * GD_SUCCESS on success or GD_FAILURE + */ +int gdAffineEqual (const double m1[6], const double m2[6]) +{ + return (fabs (m1[0] - m2[0]) < GD_EPSILON && + fabs (m1[1] - m2[1]) < GD_EPSILON && + fabs (m1[2] - m2[2]) < GD_EPSILON && + fabs (m1[3] - m2[3]) < GD_EPSILON && + fabs (m1[4] - m2[4]) < GD_EPSILON && + fabs (m1[5] - m2[5]) < GD_EPSILON); +} + diff --git a/ext/gd/php_gd.h b/ext/gd/php_gd.h index 90ebd65268f..3c60007a786 100644 --- a/ext/gd/php_gd.h +++ b/ext/gd/php_gd.h @@ -104,6 +104,7 @@ PHP_FUNCTION(imagefttext); PHP_FUNCTION(imagecreatetruecolor); PHP_FUNCTION(imagetruecolortopalette); +PHP_FUNCTION(imagepalettetotruecolor); PHP_FUNCTION(imagesetthickness); PHP_FUNCTION(imagefilledellipse); PHP_FUNCTION(imagefilledarc); @@ -127,6 +128,11 @@ PHP_FUNCTION(imageantialias); PHP_FUNCTION(imageflip); PHP_FUNCTION(imagecrop); PHP_FUNCTION(imagecropauto); +PHP_FUNCTION(imagescale); +PHP_FUNCTION(imageaffine); +PHP_FUNCTION(imageaffinematrixget); +PHP_FUNCTION(imageaffinematrixconcat); +PHP_FUNCTION(imagesetinterpolation); #endif PHP_FUNCTION(imagesetthickness); diff --git a/ext/intl/breakiterator/breakiterator_iterators.cpp b/ext/intl/breakiterator/breakiterator_iterators.cpp index d88ad8a712d..3748991aedb 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(&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/breakiterator/breakiterator_methods.cpp b/ext/intl/breakiterator/breakiterator_methods.cpp index 6a61f8cb93e..1a1b4fd1274 100644 --- a/ext/intl/breakiterator/breakiterator_methods.cpp +++ b/ext/intl/breakiterator/breakiterator_methods.cpp @@ -53,7 +53,7 @@ static void _breakiter_factory(const char *func_name, if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &locale_str, &dummy) == FAILURE) { - spprintf(&msg, NULL, "%s: bad arguments", func_name); + spprintf(&msg, 0, "%s: bad arguments", func_name); intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1 TSRMLS_CC); efree(msg); RETURN_NULL(); @@ -66,7 +66,7 @@ static void _breakiter_factory(const char *func_name, biter = func(Locale::createFromName(locale_str), status); intl_error_set_code(NULL, status TSRMLS_CC); if (U_FAILURE(status)) { - spprintf(&msg, NULL, "%s: error creating BreakIterator", + spprintf(&msg, 0, "%s: error creating BreakIterator", func_name); intl_error_set_custom_msg(NULL, msg, 1 TSRMLS_CC); efree(msg); @@ -201,7 +201,7 @@ static void _breakiter_no_args_ret_int32( object = getThis(); if (zend_parse_parameters_none() == FAILURE) { - spprintf(&msg, NULL, "%s: bad arguments", func_name); + spprintf(&msg, 0, "%s: bad arguments", func_name); intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1 TSRMLS_CC); efree(msg); RETURN_FALSE; @@ -225,7 +225,7 @@ static void _breakiter_int32_ret_int32( object = getThis(); if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &arg) == FAILURE) { - spprintf(&msg, NULL, "%s: bad arguments", func_name); + spprintf(&msg, 0, "%s: bad arguments", func_name); intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1 TSRMLS_CC); efree(msg); RETURN_FALSE; @@ -234,7 +234,7 @@ static void _breakiter_int32_ret_int32( BREAKITER_METHOD_FETCH_OBJECT; if (arg < INT32_MIN || arg > INT32_MAX) { - spprintf(&msg, NULL, "%s: offset argument is outside bounds of " + spprintf(&msg, 0, "%s: offset argument is outside bounds of " "a 32-bit wide integer", func_name); intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, msg, 1 TSRMLS_CC); efree(msg); diff --git a/ext/intl/common/common_enum.cpp b/ext/intl/common/common_enum.cpp index da47a437a67..3ba78558275 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 16e1b928791..78236fda5db 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/mysql/tests/mysql_field_flags.phpt b/ext/mysql/tests/mysql_field_flags.phpt index 7f1b366d5cb..5f8eb0995f6 100644 --- a/ext/mysql/tests/mysql_field_flags.phpt +++ b/ext/mysql/tests/mysql_field_flags.phpt @@ -45,12 +45,12 @@ mysql_free_result($res); $version = mysql_get_server_info($link); if (!preg_match('@(\d+)\.(\d+)\.(\d+)@ism', $version, $matches)) printf("[009] Cannot get server version\n"); -$version = ($matches[1] * 100) + ($matches[2] * 10) + $matches[3]; +$version = ($matches[1] * 1000) + ($matches[2] * 100) + $matches[3]; $tables = array( 'label INT, UNIQUE KEY (label)' => array( array('label', '1'), - 'label' => array(($version < 500) ? 'multiple_key' : 'unique_key') + 'label' => array(($version < 5000) ? 'multiple_key' : 'unique_key') ), 'labela INT, label2 CHAR(1), KEY keyname (labela, label2)' => array( array('labela, label2', "1, 'a'"), @@ -86,7 +86,7 @@ $tables = array( ), ); -if ($version < 560) { +if ($version < 5600) { $tables['label1 TIMESTAMP']['label1'][] = 'zerofill'; $tables['label1 TIMESTAMP']['label1'][] = 'unsigned'; } diff --git a/ext/mysql/tests/mysql_list_fields.phpt b/ext/mysql/tests/mysql_list_fields.phpt index 259a94a3996..6b6ae9b3191 100644 --- a/ext/mysql/tests/mysql_list_fields.phpt +++ b/ext/mysql/tests/mysql_list_fields.phpt @@ -74,7 +74,7 @@ mysql_field_name(): id mysql_field_type(): int Field Offset 1 mysql_field_flags()%s -mysql_field_len(): 1 +mysql_field_len(): %s mysql_field_name(): label mysql_field_type(): string done! diff --git a/ext/mysqli/mysqli_result_iterator.c b/ext/mysqli/mysqli_result_iterator.c index 0f5ccdd63dd..3ea7bafe490 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_charset.c b/ext/mysqlnd/mysqlnd_charset.c index 8766a4b25df..0ca419b5a87 100644 --- a/ext/mysqlnd/mysqlnd_charset.c +++ b/ext/mysqlnd/mysqlnd_charset.c @@ -445,12 +445,12 @@ mysqlnd_mbcharlen_utf32(unsigned int utf32 __attribute((unused))) const MYSQLND_CHARSET mysqlnd_charsets[] = { { 1, "big5","big5_chinese_ci", 1, 2, "", mysqlnd_mbcharlen_big5, check_mb_big5}, - { 3, "dec8", "dec8_swedisch_ci", 1, 1, "", NULL, NULL}, + { 3, "dec8", "dec8_swedish_ci", 1, 1, "", NULL, NULL}, { 4, "cp850", "cp850_general_ci", 1, 1, "", NULL, NULL}, { 6, "hp8", "hp8_english_ci", 1, 1, "", NULL, NULL}, { 7, "koi8r", "koi8r_general_ci", 1, 1, "", NULL, NULL}, { 8, "latin1", "latin1_swedish_ci", 1, 1, "", NULL, NULL}, - { 5, "latin1", "latin1_german_ci", 1, 1, "", NULL, NULL}, /* should be after 0x9 because swedish_ci is the default collation */ + { 5, "latin1", "latin1_german1_ci", 1, 1, "", NULL, NULL}, /* should be after 0x8 because swedish_ci is the default collation */ { 9, "latin2", "latin2_general_ci", 1, 1, "", NULL, NULL}, { 2, "latin2", "latin2_czech_cs", 1, 1, "", NULL, NULL}, /* should be after 0x9 because general_ci is the default collation */ { 10, "swe7", "swe7_swedish_ci", 1, 1, "", NULL, NULL}, @@ -485,7 +485,7 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 59, "cp1257", "cp1257_general_ci", 1, 1, "", NULL, NULL}, { 63, "binary", "binary", 1, 1, "", NULL, NULL}, { 97, "eucjpms", "eucjpms_japanese_ci", 1, 3, "", mysqlnd_mbcharlen_eucjpms, check_mb_eucjpms}, - { 29, "cp1257", "cp1257_lithunian_ci", 1, 1, "", NULL, NULL}, + { 29, "cp1257", "cp1257_lithuanian_ci", 1, 1, "", NULL, NULL}, { 31, "latin1", "latin1_german2_ci", 1, 1, "", NULL, NULL}, { 34, "cp1250", "cp1250_czech_cs", 1, 1, "", NULL, NULL}, { 42, "latin7", "latin7_general_cs", 1, 1, "", NULL, NULL}, @@ -506,19 +506,17 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 55, "utf16", "utf16_bin", 2, 4, "UTF-16 Unicode", mysqlnd_mbcharlen_utf16, check_mb_utf16}, { 56, "utf16le", "utf16le_general_ci", 2, 4, "UTF-16LE Unicode", mysqlnd_mbcharlen_utf16, check_mb_utf16}, { 58, "cp1257", "cp1257_bin", 1, 1, "", NULL, NULL}, -#ifdef USED_TO_BE_SO_BEFORE_MYSQL_5_5 - { 60, "armascii8", "armascii8_bin", 1, 1, "", NULL, NULL}, -#endif /*55*/{ 60, "utf32", "utf32_general_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32}, /*55*/{ 61, "utf32", "utf32_bin", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32}, { 62, "utf16le", "utf16le_bin", 2, 4, "UTF-16LE Unicode", mysqlnd_mbcharlen_utf16, check_mb_utf16}, + { 64, "armscii8", "armscii8_bin", 1, 1, "", NULL, NULL}, { 65, "ascii", "ascii_bin", 1, 1, "", NULL, NULL}, { 66, "cp1250", "cp1250_bin", 1, 1, "", NULL, NULL}, { 67, "cp1256", "cp1256_bin", 1, 1, "", NULL, NULL}, { 68, "cp866", "cp866_bin", 1, 1, "", NULL, NULL}, { 69, "dec8", "dec8_bin", 1, 1, "", NULL, NULL}, { 70, "greek", "greek_bin", 1, 1, "", NULL, NULL}, - { 71, "hebew", "hebrew_bin", 1, 1, "", NULL, NULL}, + { 71, "hebrew", "hebrew_bin", 1, 1, "", NULL, NULL}, { 72, "hp8", "hp8_bin", 1, 1, "", NULL, NULL}, { 73, "keybcs2", "keybcs2_bin", 1, 1, "", NULL, NULL}, { 74, "koi8r", "koi8r_bin", 1, 1, "", NULL, NULL}, @@ -558,7 +556,7 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 137, "ucs2", "ucs2_turkish_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 138, "ucs2", "ucs2_czech_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 139, "ucs2", "ucs2_danish_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, - { 140, "ucs2", "ucs2_lithunian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, + { 140, "ucs2", "ucs2_lithuanian_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 141, "ucs2", "ucs2_slovak_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 142, "ucs2", "ucs2_spanish2_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, { 143, "ucs2", "ucs2_roman_ci", 2, 2, "", mysqlnd_mbcharlen_ucs2, check_mb_ucs2}, @@ -596,7 +594,7 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = /*56*/{182, "utf32", "utf32_unicode_520_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32}, /*56*/{183, "utf32", "utf32_vietnamese_ci", 4, 4, "UTF-32 Unicode", mysqlnd_mbcharlen_utf32, check_mb_utf32}, - { 192, UTF8_MB3, UTF8_MB3"_general_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 192, UTF8_MB3, UTF8_MB3"_unicode_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 193, UTF8_MB3, UTF8_MB3"_icelandic_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 194, UTF8_MB3, UTF8_MB3"_latvian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 195, UTF8_MB3, UTF8_MB3"_romanian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, @@ -608,7 +606,7 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 201, UTF8_MB3, UTF8_MB3"_turkish_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 202, UTF8_MB3, UTF8_MB3"_czech_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 203, UTF8_MB3, UTF8_MB3"_danish_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid }, - { 204, UTF8_MB3, UTF8_MB3"_lithunian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid }, + { 204, UTF8_MB3, UTF8_MB3"_lithuanian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid }, { 205, UTF8_MB3, UTF8_MB3"_slovak_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 206, UTF8_MB3, UTF8_MB3"_spanish2_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 207, UTF8_MB3, UTF8_MB3"_roman_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, @@ -616,7 +614,7 @@ const MYSQLND_CHARSET mysqlnd_charsets[] = { 209, UTF8_MB3, UTF8_MB3"_esperanto_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 210, UTF8_MB3, UTF8_MB3"_hungarian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 211, UTF8_MB3, UTF8_MB3"_sinhala_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, - { 211, UTF8_MB3, UTF8_MB3"_german2_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, + { 212, UTF8_MB3, UTF8_MB3"_german2_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 213, UTF8_MB3, UTF8_MB3"_croatian_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 214, UTF8_MB3, UTF8_MB3"_unicode_520_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, { 215, UTF8_MB3, UTF8_MB3"_vietnamese_ci", 1, 3, "", mysqlnd_mbcharlen_utf8mb3, check_mb_utf8mb3_valid}, diff --git a/ext/mysqlnd/mysqlnd_ps.c b/ext/mysqlnd/mysqlnd_ps.c index 4916c0650d7..15b293825b4 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/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c new file mode 100644 index 00000000000..7fd986ca6de --- /dev/null +++ b/ext/opcache/Optimizer/block_pass.c @@ -0,0 +1,2032 @@ +#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) { +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + unary_op(&result, &ZEND_OP1_LITERAL(opline)); +#else + unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC); +#endif + 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 (oplineop1); + 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 00000000000..b2fb667ed55 --- /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; oplineopcode) { + 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 00000000000..dc630733faa --- /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 00000000000..3bfcec643a6 --- /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 00000000000..dc9e7319a97 --- /dev/null +++ b/ext/opcache/Optimizer/pass1_5.c @@ -0,0 +1,395 @@ +/* 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 ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + if (unary_op(&result, &ZEND_OP1_LITERAL(opline)) != SUCCESS) { +#else + if (unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC) != SUCCESS) { +#endif + 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 00000000000..30708a0935b --- /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 00000000000..fd2a1900097 --- /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; iopcodes[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; iopcodes[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; opopcode == 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]; opopcode == 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 00000000000..b0d651a5fcf --- /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 00000000000..586160c14d1 --- /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 00000000000..b574ecc81f2 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..98275a20aae --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..0097496df9a --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..03386a0a7f1 --- /dev/null +++ b/ext/opcache/README @@ -0,0 +1,206 @@ +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 and cached. + +opcache.enable_cli (default "0") + Enables the OPcache for the CLI version of PHP. It's mostly for testing + and debugging. + +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 00000000000..168082ea13a --- /dev/null +++ b/ext/opcache/ZendAccelerator.c @@ -0,0 +1,2716 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 +#endif + +#ifdef ZEND_WIN32 +typedef int uid_t; +typedef int gid_t; +#include +#endif + +#ifndef ZEND_WIN32 +# include +#else +# include +#endif + +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include +#include + +#ifndef ZEND_WIN32 +# include +# include +#endif + +#include +#include + +#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; + +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO +static char *accel_php_resolve_path(const char *filename, int filename_length, const char *path TSRMLS_DC); +#endif + +#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] == '/')); +} + +static inline int is_cachable_stream_path(const char *filename) +{ + return memcmp(filename, "file://", sizeof("file://") - 1) == 0 || + memcmp(filename, "phar://", sizeof("phar://") - 1) == 0; +} + +/* 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); + } +} + +void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason TSRMLS_DC) +{ + if ((((double) ZSMMG(wasted_shared_memory)) / ZCG(accel_directives).memory_consumption) >= ZCG(accel_directives).max_wasted_percentage) { + zend_accel_schedule_restart(reason TSRMLS_CC); + } +} + +/* 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))) { + + 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))) { + 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)); + } else { + zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC); + } + } + + 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 *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 { +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + full_path_ptr = accel_php_resolve_path(file_handle->filename, strlen(file_handle->filename), ZCG(include_path) TSRMLS_CC); +#else + full_path_ptr = accelerator_orig_zend_resolve_path(file_handle->filename, strlen(file_handle->filename) TSRMLS_CC); +#endif + if (full_path_ptr && strcmp(persistent_script->full_path, full_path_ptr) != 0) { + efree(full_path_ptr); + return FAILURE; + } + file_handle->opened_path = full_path_ptr; + } + + if (persistent_script->timestamp == 0) { + if (full_path_ptr) { + efree(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) { + efree(full_path_ptr); + file_handle->opened_path = NULL; + } + return SUCCESS; + } + if (full_path_ptr) { + efree(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 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_find(&ZCSG(include_paths), ZCG(include_path), ZCG(include_path_len) + 1) && + !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; + } else { + zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC); + } + } + + 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_if_necessary(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); + } else { + zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC); + } + } + } +} + +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_if_necessary(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_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC); + 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_if_necessary(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) { +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); +#else + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC); +#endif + zend_bailout(); + } else { +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); +#else + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC); +#endif + } + 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)) || + (is_stream_path(file_handle->filename) && + !is_cachable_stream_path(file_handle->filename))) { + /* 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) { +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename); +#else + zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename TSRMLS_CC); +#endif + zend_bailout(); + } else { +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename); +#else + zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename TSRMLS_CC); +#endif + } + 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; + if (ZSMMG(memory_exhausted)) { + zend_accel_restart_reason reason = + zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM; + zend_accel_schedule_restart_if_necessary(reason TSRMLS_CC); + } + } + 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; + if (ZSMMG(memory_exhausted)) { + zend_accel_restart_reason reason = + zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM; + zend_accel_schedule_restart_if_necessary(reason TSRMLS_CC); + } + } + 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); + } + } +#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO + zend_file_handle_dtor(file_handle); +#else + zend_file_handle_dtor(file_handle TSRMLS_CC); +#endif + 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_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(hash_restarts) = 0; + ZCSG(manual_restarts) = 0; + + ZCSG(accelerator_enabled) = 1; + ZCSG(start_time) = zend_accel_get_time(); + ZCSG(last_restart_time) = 0; + 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)); + } else { + zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM TSRMLS_CC); + } + 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) && + !ZSMMG(memory_exhausted) && + !ZCSG(restart_pending)) { + 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 00000000000..f2005473b1e --- /dev/null +++ b/ext/opcache/ZendAccelerator.h @@ -0,0 +1,383 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_ACCELERATOR_H +#define ZEND_ACCELERATOR_H + +#ifdef HAVE_CONFIG_H +# include +#endif + +#define ACCELERATOR_PRODUCT_NAME "Zend OPcache" +#define ACCELERATOR_VERSION "7.0.2-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 +# include +#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 +#else +# include +#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 +#define PHP_5_5_X_API_NO 220121212 + +/*** file locking ***/ +#ifndef ZEND_WIN32 +extern int lock_file; + +# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || (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_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 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 start_time; + 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); +void zend_accel_schedule_restart_if_necessary(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 00000000000..f9c38b1f795 --- /dev/null +++ b/ext/opcache/config.m4 @@ -0,0 +1,379 @@ +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 +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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]) + + AC_MSG_CHECKING(for known struct flock definition) + dnl Copied from ZendAccelerator.h + AC_TRY_RUN([ +#include +#include + +#ifndef ZEND_WIN32 +extern int lock_file; + +# if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || (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 +int main() { return 0; } +], [], [AC_MSG_ERROR([Don't know how to define struct flock on this system[,] set --enable-opcache=no])], []) + + 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 00000000000..280ce228c64 --- /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 00000000000..12f00554a14 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "zend_shared_alloc.h" + +#ifdef USE_MMAP + +#include +#include +#include +#include +#include + +#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 00000000000..f3377dec607 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "zend_shared_alloc.h" + +#ifdef USE_SHM_OPEN + +#include +#include +#include +#include +#include +#include +#include + +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 00000000000..d2b842304b5 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "zend_shared_alloc.h" + +#ifdef USE_SHM + +#if defined(__FreeBSD__) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 00000000000..2c3218414d1 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include "ZendAccelerator.h" +#include "zend_shared_alloc.h" +#include "zend_accelerator_util_funcs.h" +#include +#include +#include + +#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 *) mapping_base; + + 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 00000000000..c51db23f56f --- /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-- + +--FILE-- + +--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 00000000000..b1f0c6e7135 --- /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-- + +--FILE-- +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, +--EXPECT-- +OK diff --git a/ext/opcache/tests/bug64482.inc b/ext/opcache/tests/bug64482.inc new file mode 100644 index 00000000000..e3d2b210c5c --- /dev/null +++ b/ext/opcache/tests/bug64482.inc @@ -0,0 +1,2 @@ + +--FILE-- + +--EXPECT-- +Dynamic include +DYNAMIC INCLUDE diff --git a/ext/opcache/tests/issue0057.phpt b/ext/opcache/tests/issue0057.phpt new file mode 100644 index 00000000000..49e9156f155 --- /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-- + +--FILE-- +getMessage(); +} + +?> +--EXPECT-- +exception diff --git a/ext/opcache/tests/skipif.inc b/ext/opcache/tests/skipif.inc new file mode 100644 index 00000000000..c5a81810391 --- /dev/null +++ b/ext/opcache/tests/skipif.inc @@ -0,0 +1,3 @@ + diff --git a/ext/opcache/zend_accelerator_blacklist.c b/ext/opcache/zend_accelerator_blacklist.c new file mode 100644 index 00000000000..0ccd62e3441 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 (ppath); + 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 00000000000..626b8d2c476 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..93349e3d172 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include +#include +#include +#include +#ifdef ZEND_WIN32 +# include +#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 00000000000..2ff88e21d3f --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..afd227c5d54 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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; inum_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 00000000000..fdfddb40644 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..8d6ed4ee27c --- /dev/null +++ b/ext/opcache/zend_accelerator_module.c @@ -0,0 +1,645 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include + +#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; +} + +static ZEND_INI_MH(OnEnable) +{ + if (stage == ZEND_INI_STAGE_STARTUP || + stage == ZEND_INI_STAGE_SHUTDOWN || + stage == ZEND_INI_STAGE_DEACTIVATE) { + return OnUpdateBool(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); + } else { + /* It may be only temporary disabled */ + zend_bool *p; +#ifndef ZTS + char *base = (char *) mh_arg2; +#else + char *base = (char *) ts_resource(*((int *) mh_arg2)); +#endif + + p = (zend_bool *) (base+(size_t) mh_arg1); + if ((new_value_length == 2 && strcasecmp("on", new_value) == 0) || + (new_value_length == 3 && strcasecmp("yes", new_value) == 0) || + (new_value_length == 4 && strcasecmp("true", new_value) == 0) || + atoi(new_value) != 0) { + zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME " can't be temporary enabled (it may be only disabled till the end of request)"); + return FAILURE; + } else { + *p = 0; + return SUCCESS; + } + } +} + +ZEND_INI_BEGIN() + STD_PHP_INI_BOOLEAN("opcache.enable" , "1", PHP_INI_ALL, OnEnable, 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(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; inext) { + 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, "start_time", ZCSG(start_time)); + add_assoc_long(statistics, "last_restart_time", ZCSG(last_restart_time)); + add_assoc_long(statistics, "oom_restarts", ZCSG(oom_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 00000000000..539b4e85af9 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..75b1561713a --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 TSRMLS_CC); + 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 00000000000..a926145c986 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..0bffae4d0e2 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..cf3fb73cddd --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 00000000000..18af756f6e8 --- /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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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 (oplineop1.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 00000000000..ebfdea276d7 --- /dev/null +++ b/ext/opcache/zend_shared_alloc.c @@ -0,0 +1,494 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#include +#include "ZendAccelerator.h" +#include "zend_shared_alloc.h" +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#ifndef ZEND_WIN32 +# include +# include +# include +# include +# include +#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)); + if (!p_tmp_shared_globals) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + return ALLOC_FAILURE;; + } + + tmp_shared_segments = zend_shared_alloc(shared_segments_array_size + ZSMMG(shared_segments_count) * sizeof(void *)); + if (!tmp_shared_segments) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + return ALLOC_FAILURE;; + } + + 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)); + if (!ZSMMG(shared_memory_state).positions) { + zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!"); + return ALLOC_FAILURE;; + } + + 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; \ + } \ + } while (0) + +void *zend_shared_alloc(size_t size) +{ + int i; + unsigned int block_size = ZEND_ALIGNED_SIZE(size); + 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 */ + void *retval = (void *) (((char *) ZSMMG(shared_segments)[i]->p) + ZSMMG(shared_segments)[i]->pos); + + ZSMMG(shared_segments)[i]->pos += block_size; + ZSMMG(shared_free) -= block_size; + memset(retval, 0, block_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 00000000000..b7f36299bf5 --- /dev/null +++ b/ext/opcache/zend_shared_alloc.h @@ -0,0 +1,182 @@ +/* + +----------------------------------------------------------------------+ + | 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 | + | Zeev Suraski | + | Stanislav Malyshev | + | Dmitry Stogov | + +----------------------------------------------------------------------+ +*/ + +#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_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 b1ee89a8a49..7d34d9feb15 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 5dc445ff8de..1b0db91c380 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 f2e36c1719e..2ae559571d4 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/pdo_pgsql/tests/is_in_transaction.phpt b/ext/pdo_pgsql/tests/is_in_transaction.phpt index 99ff56162d8..72da4f4e079 100644 --- a/ext/pdo_pgsql/tests/is_in_transaction.phpt +++ b/ext/pdo_pgsql/tests/is_in_transaction.phpt @@ -57,10 +57,10 @@ var_dump($db->inTransaction()); ?> --EXPECT-- Test PDO::PGSQL_TRANSACTION_INTRANS -int(2) +bool(true) Test PDO::PGSQL_TRANSACTION_IDLE -int(0) +bool(false) Test PDO::PGSQL_TRANSACTION_INERROR -int(3) +bool(true) Test PDO::PGSQL_TRANSACTION_IDLE -int(0) +bool(false) diff --git a/ext/pgsql/tests/bug46408.phpt b/ext/pgsql/tests/bug46408.phpt index 8c72ba5f3eb..bf842909073 100644 --- a/ext/pgsql/tests/bug46408.phpt +++ b/ext/pgsql/tests/bug46408.phpt @@ -3,6 +3,9 @@ Bug #46408 (Locale number format settings can cause pg_query_params to break wit --SKIPIF-- --FILE-- 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 d6c469d9cb7..c03576cb2c9 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 4842d279133..d28f136f77b 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'] = ''; $files['b.php'] = ''; $files['b/c.php'] = ''; + +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 13a8d0db298..0d4eb1e2aa2 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'] = ''; $files['b.php'] = ''; $files['b/c.php'] = ''; + +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/phpinfo_001.phpt b/ext/phar/tests/phpinfo_001.phpt index 5de74dac0ef..d99ccd18031 100644 --- a/ext/phar/tests/phpinfo_001.phpt +++ b/ext/phar/tests/phpinfo_001.phpt @@ -26,7 +26,7 @@ phpinfo(INFO_MODULES); Phar: PHP Archive support => enabled Phar EXT version => %s Phar API version => 1.1.1 -SVN revision => %sRevision: %s $ +SVN revision => %sId: %s $ Phar-based phar archives => enabled Tar-based phar archives => enabled ZIP-based phar archives => enabled @@ -48,7 +48,7 @@ Phar Phar: PHP Archive support => enabled Phar EXT version => %s Phar API version => 1.1.1 -SVN revision => %sRevision: %s $ +SVN revision => %sId: %s $ Phar-based phar archives => enabled Tar-based phar archives => enabled ZIP-based phar archives => enabled diff --git a/ext/phar/tests/phpinfo_002.phpt b/ext/phar/tests/phpinfo_002.phpt index da6db9571b4..ef505fedad5 100644 --- a/ext/phar/tests/phpinfo_002.phpt +++ b/ext/phar/tests/phpinfo_002.phpt @@ -24,7 +24,7 @@ Phar Phar: PHP Archive support => enabled Phar EXT version => %s Phar API version => 1.1.1 -SVN revision => %sRevision: %s $ +SVN revision => %sId: %s $ Phar-based phar archives => enabled Tar-based phar archives => enabled ZIP-based phar archives => enabled diff --git a/ext/phar/tests/phpinfo_004.phpt b/ext/phar/tests/phpinfo_004.phpt index c49205c1475..24263f07bee 100644 --- a/ext/phar/tests/phpinfo_004.phpt +++ b/ext/phar/tests/phpinfo_004.phpt @@ -29,7 +29,7 @@ phpinfo(INFO_MODULES); Phar: PHP Archive supportenabled Phar EXT version %s Phar API version 1.1.1 -SVN revision %sRevision: %s $ +SVN revision %sId: %s $ Phar-based phar archives enabled Tar-based phar archives enabled ZIP-based phar archives enabled @@ -53,7 +53,7 @@ Phar based on pear/PHP_Archive, original concept by Davey Shafik.
Phar full Phar: PHP Archive supportenabled Phar EXT version %s Phar API version 1.1.1 -SVN revision %sRevision: %s $ +SVN revision %sId: %s $ Phar-based phar archives enabled Tar-based phar archives enabled ZIP-based phar archives enabled diff --git a/ext/phar/tests/tar/create_new_and_modify.phpt b/ext/phar/tests/tar/create_new_and_modify.phpt index 8062fda769b..905bfabc828 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 91ef4a20462..1982b6bda9c 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'] = ''; $phar->setStub(''); $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 707bcbd0eda..7593ebc1fb0 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'] = ''; $phar->setStub(''); $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 5a3ec3317b3..55d69cca0e2 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 b7bda7ca4b6..f01280013f0 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'] = ''; $phar->setStub(''); $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 fdd0b42b5cb..00804991770 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'] = ''; $phar->setStub(''); $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/php_reflection.c b/ext/reflection/php_reflection.c index 15befa2fc72..b65ccaa26d9 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1298,7 +1298,8 @@ static void reflection_method_factory(zend_class_entry *ce, zend_function *metho } MAKE_STD_ZVAL(name); MAKE_STD_ZVAL(classname); - ZVAL_STRING(name, method->common.function_name, 1); + ZVAL_STRING(name, (method->common.scope && method->common.scope->trait_aliases)? + zend_resolve_method_name(ce, method) : method->common.function_name, 1); ZVAL_STRINGL(classname, method->common.scope->name, method->common.scope->name_length, 1); reflection_instantiate(reflection_method_ptr, object TSRMLS_CC); intern = (reflection_object *) zend_object_store_get_object(object TSRMLS_CC); diff --git a/ext/reflection/tests/005.phpt b/ext/reflection/tests/005.phpt index f337e44ae68..e257699b6f8 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-- +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- Steve Seear +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- Steve Seear +--INI-- +opcache.save_comments=1 +opcache.load_comments=1 --FILE-- getMethods()); +print_r(($method = $obj->getMethod("Bmethod"))); +var_dump($method->getName()); +var_dump($method->getShortName()); +?> +--EXPECT-- +Array +( + [0] => ReflectionMethod Object + ( + [name] => Bmethod + [class] => A + ) + + [1] => ReflectionMethod Object + ( + [name] => t2method + [class] => A + ) + +) +ReflectionMethod Object +( + [name] => Bmethod + [class] => A +) +string(7) "Bmethod" +string(7) "Bmethod" diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 692516840b2..e7c2f298447 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; + + if (curnode) { + ZVAL_STRINGL(key, (char *) curnode->name, xmlStrlen(curnode->name), 1); + } else { + ZVAL_NULL(key); } - - namelen = xmlStrlen(curnode->name); - *str_key = estrndup((char *)curnode->name, namelen); - *str_key_len = namelen + 1; - return HASH_KEY_IS_STRING; - } /* }}} */ diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 6f2f070f21c..9789638c66d 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 12879416c62..f5239e11789 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 06b6492bd7f..522d417aff0 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 897956d91b7..5cec3e558eb 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/soap/tests/bugs/bug34657.phpt b/ext/soap/tests/bugs/bug34657.phpt index d974d02cc6a..9b67ec69ad8 100644 --- a/ext/soap/tests/bugs/bug34657.phpt +++ b/ext/soap/tests/bugs/bug34657.phpt @@ -25,11 +25,6 @@ try { } ?> --EXPECTF-- -Warning: SoapClient::SoapClient(): %s %sbug34657.php on line 3 - -Warning: SoapClient::SoapClient(http://i_dont_exist.com/some.wsdl): failed to open stream: %sbug34657.php on line 3 - -Warning: SoapClient::SoapClient(): I/O warning : failed to load external entity "http://i_dont_exist.com/some.wsdl" in %sbug34657.php on line 3 SoapFault -SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://i_dont_exist.com/some.wsdl'%S +SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://i_dont_exist.com/some.wsdl'%A ok diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index e56a8f09e2a..edeade37556 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 f43a3709e13..cf653f6899b 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 a8417feada3..aa462dfd764 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; + } + + 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; + } + + Z_ADDREF_P(value); + 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 fec7e2c4aca..86a5371ed30 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 d2de85b2a78..cb1f68dcf11 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 87b3763cfd7..fcb4d20a639 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 39cc0d1337d..73d9d2e6147 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 00000000000..b94b067f4d8 --- /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-- +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 00000000000..12cfe40008b --- /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-- +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 00000000000..c9c319316fd --- /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-- +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 00000000000..396f89b491e --- /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-- +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 00000000000..b60f0639249 --- /dev/null +++ b/ext/spl/tests/dllist_013.phpt @@ -0,0 +1,45 @@ +--TEST-- +SPL: DoublyLinkedList: insert operations +--FILE-- +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=== + +--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 00000000000..4ca9485faf5 --- /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-- + 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 edd03f50408..eb77f79371b 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 40a27c05362..226bfefc5be 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 a40fdd23979..3a84d16d687 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/head.c b/ext/standard/head.c index fa578175b19..5310ff6c03b 100644 --- a/ext/standard/head.c +++ b/ext/standard/head.c @@ -132,7 +132,7 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t efree(dt); efree(cookie); efree(encoded_value); - zend_error(E_WARNING, "Expiry date cannot have a year greater then 9999"); + zend_error(E_WARNING, "Expiry date cannot have a year greater than 9999"); return FAILURE; } strlcat(cookie, dt, len + 100); diff --git a/ext/standard/info.c b/ext/standard/info.c index 20e6b0c4a21..e8ba908070b 100644 --- a/ext/standard/info.c +++ b/ext/standard/info.c @@ -316,7 +316,7 @@ char* php_get_windows_name() } if (VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && osvi.dwMajorVersion > 4 ) { - if (osvi.dwMajorVersion == 6) { + if (osvi.dwMajorVersion == 6) { if( osvi.dwMinorVersion == 0 ) { if( osvi.wProductType == VER_NT_WORKSTATION ) { major = "Windows Vista"; @@ -330,6 +330,12 @@ char* php_get_windows_name() } else { major = "Windows Server 2008 R2"; } + } else if ( osvi.dwMinorVersion == 2 ) { + if( osvi.wProductType == VER_NT_WORKSTATION ) { + major = "Windows 8"; + } else { + major = "Windows Server 2012"; + } } else { major = "Unknown Windows version"; } diff --git a/ext/standard/php_array.h b/ext/standard/php_array.h index 942c33f9e81..1cf27790718 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/string.c b/ext/standard/string.c index e245ce3fb6d..f3f78100b4c 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -1715,7 +1715,7 @@ PHP_FUNCTION(stristr) if (Z_TYPE_P(needle) == IS_STRING) { char *orig_needle; if (!Z_STRLEN_P(needle)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty needle"); efree(haystack_dup); RETURN_FALSE; } @@ -1765,7 +1765,7 @@ PHP_FUNCTION(strstr) if (Z_TYPE_P(needle) == IS_STRING) { if (!Z_STRLEN_P(needle)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty delimiter"); + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty needle"); RETURN_FALSE; } 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 00000000000..70ce2136b44 --- /dev/null +++ b/ext/standard/tests/array/array_column_basic.phpt @@ -0,0 +1,307 @@ +--TEST-- +Test array_column() function: basic functionality +--FILE-- + 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 00000000000..1aec1acc636 --- /dev/null +++ b/ext/standard/tests/array/array_column_error.phpt @@ -0,0 +1,98 @@ +--TEST-- +Test array_column() function: error conditions +--FILE-- + +--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/bug64370_var1.phpt b/ext/standard/tests/bug64370_var1.phpt new file mode 100644 index 00000000000..aca46a594d1 --- /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-- += 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 00000000000..d0d3590ea7b --- /dev/null +++ b/ext/standard/tests/bug64370_var2.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test bug #64370 sequential microtime(true) calls +--FILE-- + $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/file/parse_ini_file.phpt b/ext/standard/tests/file/parse_ini_file.phpt index dab07e56e70..db14c7a9b43 100644 --- a/ext/standard/tests/file/parse_ini_file.phpt +++ b/ext/standard/tests/file/parse_ini_file.phpt @@ -57,15 +57,15 @@ Non_alpha4 = % Non_alpha5 = <> Non_alpha6 = @ Non_alpha7 = # -Non_alpha8 = ^ -Non_alpha9 = - -Non_alpha10 = : -Non_alpha11 = ? -Non_alpha12 = / -Non_alpha13 = \ +Non_alpha8 = - +Non_alpha9 = : +Non_alpha10 = ? +Non_alpha11 = / +Non_alpha12 = \ ;These chars have a special meaning when used in the value, ; hence parser throws an error -;Non_alpha14 = & +;Non_alpha13 = & +;Non_alpha14 = ^ ;Non_alpha15 = {} ;Non_alpha16 = | ;Non_alpha17 = ~ @@ -249,12 +249,11 @@ Array [Non_alpha5] => <> [Non_alpha6] => @ [Non_alpha7] => # - [Non_alpha8] => ^ - [Non_alpha9] => - - [Non_alpha10] => : - [Non_alpha11] => ? - [Non_alpha12] => / - [Non_alpha13] => \ + [Non_alpha8] => - + [Non_alpha9] => : + [Non_alpha10] => ? + [Non_alpha11] => / + [Non_alpha12] => \ [Non_alpha1_quotes] => ; [Non_alpha2_quotes] => + [Non_alpha3_quotes] => * @@ -385,12 +384,11 @@ Array [Non_alpha5] => <> [Non_alpha6] => @ [Non_alpha7] => # - [Non_alpha8] => ^ - [Non_alpha9] => - - [Non_alpha10] => : - [Non_alpha11] => ? - [Non_alpha12] => / - [Non_alpha13] => \ + [Non_alpha8] => - + [Non_alpha9] => : + [Non_alpha10] => ? + [Non_alpha11] => / + [Non_alpha12] => \ [Non_alpha1_quotes] => ; [Non_alpha2_quotes] => + [Non_alpha3_quotes] => * diff --git a/ext/standard/tests/general_functions/parse_ini_string_001.phpt b/ext/standard/tests/general_functions/parse_ini_string_001.phpt index e135210aa6d..c5e70cb9004 100644 --- a/ext/standard/tests/general_functions/parse_ini_string_001.phpt +++ b/ext/standard/tests/general_functions/parse_ini_string_001.phpt @@ -55,15 +55,15 @@ Non_alpha4 = % Non_alpha5 = <> Non_alpha6 = @ Non_alpha7 = # -Non_alpha8 = ^ -Non_alpha9 = - -Non_alpha10 = : -Non_alpha11 = ? -Non_alpha12 = / -Non_alpha13 = \ +Non_alpha8 = - +Non_alpha9 = : +Non_alpha10 = ? +Non_alpha11 = / +Non_alpha12 = \ ;These chars have a special meaning when used in the value, ; hence parser throws an error -;Non_alpha14 = & +;Non_alpha13 = & +;Non_alpha14 = ^ ;Non_alpha15 = {} ;Non_alpha16 = | ;Non_alpha17 = ~ @@ -239,12 +239,11 @@ Array [Non_alpha5] => <> [Non_alpha6] => @ [Non_alpha7] => # - [Non_alpha8] => ^ - [Non_alpha9] => - - [Non_alpha10] => : - [Non_alpha11] => ? - [Non_alpha12] => / - [Non_alpha13] => \ + [Non_alpha8] => - + [Non_alpha9] => : + [Non_alpha10] => ? + [Non_alpha11] => / + [Non_alpha12] => \ [Non_alpha1_quotes] => ; [Non_alpha2_quotes] => + [Non_alpha3_quotes] => * @@ -375,12 +374,11 @@ Array [Non_alpha5] => <> [Non_alpha6] => @ [Non_alpha7] => # - [Non_alpha8] => ^ - [Non_alpha9] => - - [Non_alpha10] => : - [Non_alpha11] => ? - [Non_alpha12] => / - [Non_alpha13] => \ + [Non_alpha8] => - + [Non_alpha9] => : + [Non_alpha10] => ? + [Non_alpha11] => / + [Non_alpha12] => \ [Non_alpha1_quotes] => ; [Non_alpha2_quotes] => + [Non_alpha3_quotes] => * diff --git a/ext/standard/tests/serialize/bug64354_1.phpt b/ext/standard/tests/serialize/bug64354_1.phpt new file mode 100644 index 00000000000..e85749bcbef --- /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-- +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 00000000000..41a455b54a6 --- /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-- +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 00000000000..3ce61152d64 --- /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-- +getMessage()); +} +?> +--EXPECTF-- +string(6) "Failed" diff --git a/ext/standard/tests/strings/stristr_error.phpt b/ext/standard/tests/strings/stristr_error.phpt index 831d93f4ec3..2da35de7358 100644 --- a/ext/standard/tests/strings/stristr_error.phpt +++ b/ext/standard/tests/strings/stristr_error.phpt @@ -50,11 +50,11 @@ NULL -- Testing stristr() function with empty haystack -- -Warning: stristr(): Empty delimiter in %s on line %d +Warning: stristr(): Empty needle in %s on line %d bool(false) -- Testing stristr() function with empty needle -- -Warning: stristr(): Empty delimiter in %s on line %d +Warning: stristr(): Empty needle in %s on line %d bool(false) ===DONE=== diff --git a/ext/standard/tests/strings/strstr.phpt b/ext/standard/tests/strings/strstr.phpt index 1c8d753e658..b135258f882 100644 Binary files a/ext/standard/tests/strings/strstr.phpt and b/ext/standard/tests/strings/strstr.phpt differ diff --git a/ext/standard/var.c b/ext/standard/var.c index b13edf6615c..f76a14cfa66 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 3730ff8e813..ddf43b02cf1 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 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -396,8 +396,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); @@ -427,7 +432,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) -#line 431 "ext/standard/var_unserializer.c" +#line 436 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -487,9 +492,9 @@ yy2: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 759 "ext/standard/var_unserializer.re" +#line 785 "ext/standard/var_unserializer.re" { return 0; } -#line 493 "ext/standard/var_unserializer.c" +#line 498 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -532,13 +537,13 @@ yy13: goto yy3; yy14: ++YYCURSOR; -#line 753 "ext/standard/var_unserializer.re" +#line 779 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 542 "ext/standard/var_unserializer.c" +#line 547 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -568,7 +573,7 @@ yy20: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 630 "ext/standard/var_unserializer.re" +#line 635 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -623,10 +628,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')) { @@ -644,6 +658,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; @@ -655,6 +675,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) { @@ -691,7 +717,7 @@ yy20: return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 695 "ext/standard/var_unserializer.c" +#line 721 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -716,7 +742,7 @@ yy27: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 622 "ext/standard/var_unserializer.re" +#line 627 "ext/standard/var_unserializer.re" { INIT_PZVAL(*rval); @@ -724,7 +750,7 @@ yy27: return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 728 "ext/standard/var_unserializer.c" +#line 754 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -745,7 +771,7 @@ yy34: yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 602 "ext/standard/var_unserializer.re" +#line 607 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -765,7 +791,7 @@ yy34: return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 769 "ext/standard/var_unserializer.c" +#line 795 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -786,7 +812,7 @@ yy41: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 573 "ext/standard/var_unserializer.re" +#line 578 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -815,7 +841,7 @@ yy41: ZVAL_STRINGL(*rval, str, len, 0); return 1; } -#line 819 "ext/standard/var_unserializer.c" +#line 845 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -836,7 +862,7 @@ yy48: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 545 "ext/standard/var_unserializer.re" +#line 550 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -864,7 +890,7 @@ yy48: ZVAL_STRINGL(*rval, str, len, 1); return 1; } -#line 868 "ext/standard/var_unserializer.c" +#line 894 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -952,7 +978,7 @@ yy61: } yy63: ++YYCURSOR; -#line 535 "ext/standard/var_unserializer.re" +#line 540 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -962,7 +988,7 @@ use_double: ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 966 "ext/standard/var_unserializer.c" +#line 992 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1021,7 +1047,7 @@ yy73: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 520 "ext/standard/var_unserializer.re" +#line 525 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); @@ -1036,7 +1062,7 @@ yy73: return 1; } -#line 1040 "ext/standard/var_unserializer.c" +#line 1066 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1063,7 +1089,7 @@ yy79: if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 493 "ext/standard/var_unserializer.re" +#line 498 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1090,7 +1116,7 @@ yy79: ZVAL_LONG(*rval, parse_iv(start + 2)); return 1; } -#line 1094 "ext/standard/var_unserializer.c" +#line 1120 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1098,24 +1124,24 @@ yy83: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 486 "ext/standard/var_unserializer.re" +#line 491 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); return 1; } -#line 1109 "ext/standard/var_unserializer.c" +#line 1135 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 479 "ext/standard/var_unserializer.re" +#line 484 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); return 1; } -#line 1119 "ext/standard/var_unserializer.c" +#line 1145 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1138,7 +1164,7 @@ yy91: if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 456 "ext/standard/var_unserializer.re" +#line 461 "ext/standard/var_unserializer.re" { long id; @@ -1161,7 +1187,7 @@ yy91: return 1; } -#line 1165 "ext/standard/var_unserializer.c" +#line 1191 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1184,7 +1210,7 @@ yy97: if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 435 "ext/standard/var_unserializer.re" +#line 440 "ext/standard/var_unserializer.re" { long id; @@ -1205,9 +1231,9 @@ yy97: return 1; } -#line 1209 "ext/standard/var_unserializer.c" +#line 1235 "ext/standard/var_unserializer.c" } -#line 761 "ext/standard/var_unserializer.re" +#line 787 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 204995783fe..4d99cbfd789 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 362f92d749c..576be3b3536 100644 --- a/ext/zip/lib/zip_close.c +++ b/ext/zip/lib/zip_close.c @@ -164,9 +164,10 @@ zip_close(struct zip *za) for (j=0; jentry+i) || new_torrentzip) { - _zip_dirent_init(&de); if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) _zip_dirent_torrent_normalize(&de); @@ -192,7 +193,7 @@ zip_close(struct zip *za) } else { /* copy existing directory entries */ - if (fseeko(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) { + if ((NULL == za->zp) || (fseeko(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0)) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); error = 1; break; diff --git a/ext/zip/lib/zip_dirent.c b/ext/zip/lib/zip_dirent.c index b5b9d273be4..b9dac5c989e 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/main/php_ini.c b/main/php_ini.c index b15a3846dd3..cb2c7ea8089 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/win32_internal_function_disabled.h b/main/win32_internal_function_disabled.h index 3a78f6c598f..80fd8d1b827 100644 --- a/main/win32_internal_function_disabled.h +++ b/main/win32_internal_function_disabled.h @@ -23,12 +23,14 @@ /* Windows Server 2008 6.0 Windows Vista 6.0 + +Verssions below are not supported anymore, php won't even load: Windows Server 2003 R2 5.2 Windows Server 2003 5.2 Windows XP 5.1 Windows 2000 5.0 */ -static const char *function_name_5[] = {"link", NULL}; -const int function_name_cnt_5 = 1; +static const char *function_name_5[] = {NULL}; +const int function_name_cnt_5 = 0; static const char *function_name_6[] = {"readlink", "symlink", NULL}; const int function_name_cnt_6 = 2; diff --git a/makedist b/makedist index bce40a94880..f5823d39251 100755 --- a/makedist +++ b/makedist @@ -35,7 +35,10 @@ if test "${1}" = "1" -a "${2}" -lt "28"; then fi IFS="$old_IFS" -PHPROOT=git@git.php.net:php-src.git +if test "$PHPROOTx" == "x"; then + PHPROOT=git@git.php.net:php-src.git; +fi + LT_TARGETS='ltconfig ltmain.sh config.guess config.sub' if echo '\c' | grep -s c >/dev/null 2>&1 diff --git a/php.ini-development b/php.ini-development index a9c2d06e675..eedb1daa73d 100644 --- a/php.ini-development +++ b/php.ini-development @@ -1862,6 +1862,102 @@ ldap.max_links = -1 [dba] ;dba.default_handler= +[opcache] +; Determines if Zend OPCache is enabled +;opcache.enable=0 + +; Determines if Zend OPCache is enabled for the CLI version of PHP +;opcache.enable_cli=0 + +; The OPcache shared memory storage size. +;opcache.memory_consumption=64 + +; The amount of memory for interned strings in Mbytes. +;opcache.interned_strings_buffer=4 + +; The maximum number of keys (scripts) in the OPcache hash table. +; Only numbers between 200 and 100000 are allowed. +;opcache.max_accelerated_files=2000 + +; The maximum percentage of "wasted" memory until a restart is scheduled. +;opcache.max_wasted_percentage=5 + +; 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.use_cwd=1 + +; When disabled, you must reset the OPcache manually or restart the +; webserver for changes to the filesystem to take effect. +;opcache.validate_timestamps=1 + +; 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_freq=2 + +; Enables or disables file search in include_path optimization +;opcache.revalidate_path=0 + +; If disabled, all PHPDoc comments are dropped from the code to reduce the + ;size of the optimized code. +;opcache.save_comments=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.load_comments=1 + +; If enabled, a fast shutdown sequence is used for the accelerated code +;opcache.fast_shutdown=0 + +; Allow file existence override (file_exists, etc.) performance feature. +;opcache.enable_file_override=0 + +; A bitmask, where each bit enables or disables the appropriate OPcache +; passes +;opcache.optimization_level=0xffffffff + +;opcache.inherited_hack=1 +;opcache.dups_fix=0 + +; 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'). +;opcache.blacklist_filename= + +; Allows exclusion of large files from being cached. By default all files +; are cached. +;opcache.max_file_size=0 + +; Check the cache checksum each N requests. +; The default value of "0" means that the checks are disabled. +;opcache.consistency_checks=0 + +; How long to wait (in seconds) for a scheduled restart to begin if the cache +; is not being accessed. +;opcache.force_restart_timeout=180 + +; OPcache error_log file name. Empty string assumes "stderr". +;opcache.error_log= + +; 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.log_verbosity_level=1 + +; Preferred Shared Memory back-end. Leave empty and let the system decide. +;opcache.preferred_memory_model= + +; Protect the shared memory from unexpected writing during script execution. +; Useful for internal debugging only. +;opcache.protect_memory=0 + ; Local Variables: ; tab-width: 4 ; End: diff --git a/php.ini-production b/php.ini-production index 76d5fb5dd6c..d549ae2d80a 100644 --- a/php.ini-production +++ b/php.ini-production @@ -1863,6 +1863,102 @@ ldap.max_links = -1 [dba] ;dba.default_handler= +[opcache] +; Determines if Zend OPCache is enabled +;opcache.enable=0 + +; Determines if Zend OPCache is enabled for the CLI version of PHP +;opcache.enable_cli=0 + +; The OPcache shared memory storage size. +;opcache.memory_consumption=64 + +; The amount of memory for interned strings in Mbytes. +;opcache.interned_strings_buffer=4 + +; The maximum number of keys (scripts) in the OPcache hash table. +; Only numbers between 200 and 100000 are allowed. +;opcache.max_accelerated_files=2000 + +; The maximum percentage of "wasted" memory until a restart is scheduled. +;opcache.max_wasted_percentage=5 + +; 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.use_cwd=1 + +; When disabled, you must reset the OPcache manually or restart the +; webserver for changes to the filesystem to take effect. +;opcache.validate_timestamps=1 + +; 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_freq=2 + +; Enables or disables file search in include_path optimization +;opcache.revalidate_path=0 + +; If disabled, all PHPDoc comments are dropped from the code to reduce the + ;size of the optimized code. +;opcache.save_comments=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.load_comments=1 + +; If enabled, a fast shutdown sequence is used for the accelerated code +;opcache.fast_shutdown=0 + +; Allow file existence override (file_exists, etc.) performance feature. +;opcache.enable_file_override=0 + +; A bitmask, where each bit enables or disables the appropriate OPcache +; passes +;opcache.optimization_level=0xffffffff + +;opcache.inherited_hack=1 +;opcache.dups_fix=0 + +; 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'). +;opcache.blacklist_filename= + +; Allows exclusion of large files from being cached. By default all files +; are cached. +;opcache.max_file_size=0 + +; Check the cache checksum each N requests. +; The default value of "0" means that the checks are disabled. +;opcache.consistency_checks=0 + +; How long to wait (in seconds) for a scheduled restart to begin if the cache +; is not being accessed. +;opcache.force_restart_timeout=180 + +; OPcache error_log file name. Empty string assumes "stderr". +;opcache.error_log= + +; 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.log_verbosity_level=1 + +; Preferred Shared Memory back-end. Leave empty and let the system decide. +;opcache.preferred_memory_model= + +; Protect the shared memory from unexpected writing during script execution. +; Useful for internal debugging only. +;opcache.protect_memory=0 + ; Local Variables: ; tab-width: 4 ; End: diff --git a/run-tests.php b/run-tests.php index 8b7fa7f8be5..84907a705f8 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/php.1.in b/sapi/cli/php.1.in index 186b128f89c..0e9d07ac771 100644 --- a/sapi/cli/php.1.in +++ b/sapi/cli/php.1.in @@ -1,4 +1,4 @@ -.TH PHP 1 "2010" "The PHP Group" "Scripting Language" +.TH PHP 1 "2013" "The PHP Group" "Scripting Language" .SH NAME php \- PHP Command Line Interface 'CLI' .SH SYNOPSIS @@ -42,6 +42,12 @@ php \- PHP Command Line Interface 'CLI' .LP \fBphp \fP[options] \fB\-a\fP .LP +.B php +[options] \-S +.IR addr:port +[\-t +.IR docroot ] +.LP .SH DESCRIPTION \fBPHP\fP is a widely\-used general\-purpose scripting language that is especially suited for Web development and can be embedded into HTML. This is the command line interface @@ -78,7 +84,13 @@ and therefore reading from .B STDIN explicitly changes the next input line or skips input lines. .LP -If none of \-r \-f \-B \-R \-F or \-E is present but a single parameter is given +PHP also contains an embedded web server for application development purpose. By using the \-S option where +.B addr:port +point to a local address and port PHP will listen to HTTP requests on that address and port and serve files from the current working directory or the +.B docroot +passed by the \-t option. +.LP +If none of \-r \-f \-B \-R \-F \-E or \-S is present but a single parameter is given then this parameter is taken as the filename to parse and execute (same as with \-f). If no parameter is present then the standard input is read and executed. @@ -263,6 +275,20 @@ after processing all input lines Output HTML syntax highlighted source .TP .PD 0 +.B \-\-server \fIaddr:port\fP +.TP +.PD 1 +.B \-S \fIaddr:port\fP +Start embedded Webserver on the given local address and port +.TP +.PD 0 +.B \-\-docroot \fIdocroot\fP +.TP +.PD 1 +.B \-t \fIdocroot\fP +Specify the document root to be used by the embedded web server +.TP +.PD 0 .B \-\-version .TP .PD 1 @@ -435,7 +461,7 @@ contributors all around the world. .SH VERSION INFORMATION This manpage describes \fBphp\fP, version @PHP_VERSION@. .SH COPYRIGHT -Copyright \(co 1997\-2010 The PHP Group +Copyright \(co 1997\-2013 The PHP Group .LP 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 diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index 4b8bae7f78a..729052334d6 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1402,7 +1402,7 @@ out: * Do not move this de-initialization. It needs to happen right before * exiting. */ - cleanup_ps_args(argv); + cleanup_ps_args(argv); exit(exit_status); } /* }}} */ diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c index a2b85d47b0f..5c9b2e86de0 100644 --- a/sapi/cli/php_cli_server.c +++ b/sapi/cli/php_cli_server.c @@ -24,11 +24,12 @@ #include #ifdef PHP_WIN32 -#include -#include -#include "win32/time.h" -#include "win32/signal.h" -#include "win32/php_registry.h" +# include +# include +# include "win32/time.h" +# include "win32/signal.h" +# include "win32/php_registry.h" +# include #else # include "php_config.h" #endif @@ -296,6 +297,36 @@ static const char php_cli_server_css[] = "\n"; /* }}} */ +#ifdef PHP_WIN32 +int php_cli_server_get_system_time(char *buf) { + struct _timeb system_time; + errno_t err; + + if (buf == NULL) { + return -1; + } + + _ftime(&system_time); + err = ctime_s(buf, 52, &(system_time.time) ); + if (err) { + return -1; + } + return 0; +} +#else +int php_cli_server_get_system_time(char *buf) { + struct timeval tv; + struct tm tm; + + gettimeofday(&tv, NULL); + + /* TODO: should be checked for NULL tm/return vaue */ + php_localtime_r(&tv.tv_sec, &tm); + php_asctime_r(&tm, buf); + return 0; +} +#endif + static void char_ptr_dtor_p(char **p) /* {{{ */ { pefree(*p, 1); @@ -640,13 +671,11 @@ static void sapi_cli_server_register_variables(zval *track_vars_array TSRMLS_DC) static void sapi_cli_server_log_message(char *msg TSRMLS_DC) /* {{{ */ { - struct timeval tv; - struct tm tm; char buf[52]; - gettimeofday(&tv, NULL); - php_localtime_r(&tv.tv_sec, &tm); - php_asctime_r(&tm, buf); - { + + if (php_cli_server_get_system_time(buf) != 0) { + memmove(buf, "unknown time, can't be fetched", sizeof("unknown time, can't be fetched")); + } else { size_t l = strlen(buf); if (l > 0) { buf[l - 1] = '\0'; @@ -2402,12 +2431,12 @@ int do_cli_server(int argc, char **argv TSRMLS_DC) /* {{{ */ sapi_module.phpinfo_as_text = 0; { - struct timeval tv; - struct tm tm; char buf[52]; - gettimeofday(&tv, NULL); - php_localtime_r(&tv.tv_sec, &tm); - php_asctime_r(&tm, buf); + + if (php_cli_server_get_system_time(buf) != 0) { + memmove(buf, "unknown time, can't be fetched", sizeof("unknown time, can't be fetched")); + } + printf("PHP %s Development Server started at %s" "Listening on http://%s\n" "Document root is %s\n" diff --git a/sapi/cli/ps_title.c b/sapi/cli/ps_title.c index a2e47f03165..730a31b11e7 100644 --- a/sapi/cli/ps_title.c +++ b/sapi/cli/ps_title.c @@ -124,6 +124,11 @@ static size_t ps_buffer_cur_len; /* actual string length in ps_buffer */ static int save_argc; static char** save_argv; +/* + * This holds the 'locally' allocated environ from the save_ps_args method. + * This is subsequently free'd at exit. + */ +static char** new_environ; /* * Call this method early, before any code has used the original argv passed in @@ -145,7 +150,6 @@ char** save_ps_args(int argc, char** argv) { char* end_of_area = NULL; int non_contiguous_area = 0; - char** new_environ; int i; /* @@ -405,9 +409,9 @@ void cleanup_ps_args(char **argv) #ifdef PS_USE_CLOBBER_ARGV { int i; - for (i = 0; environ[i] != NULL; i++) - free(environ[i]); - free(environ); + for (i = 0; new_environ[i] != NULL; i++) + free(new_environ[i]); + free(new_environ); } #endif /* PS_USE_CLOBBER_ARGV */ diff --git a/sapi/cli/tests/bug64529.phpt b/sapi/cli/tests/bug64529.phpt new file mode 100644 index 00000000000..d3755724e8a --- /dev/null +++ b/sapi/cli/tests/bug64529.phpt @@ -0,0 +1,67 @@ +--TEST-- +Bug #64529 (Ran out of opcode space) +--SKIPIF-- + +--FILE-- +" + +send "echo 'hello world';\n" +send "\04" + +expect eof + +exit + +SCRIPT; + +} else { + $expect_script = <<