mirror of
https://github.com/bdnugget/php-src.git
synced 2026-11-08 09:47:30 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62129f31a7 | ||
|
|
793b52b576 | ||
|
|
8b9d23c0cf | ||
|
|
0f679b926c | ||
|
|
99e7d1dca2 | ||
|
|
21d7efef92 | ||
|
|
9657591e35 |
No files matched your search
@@ -1,60 +0,0 @@
|
||||
version: "{branch}.build.{build}"
|
||||
|
||||
image: Visual Studio 2019
|
||||
|
||||
branches:
|
||||
except:
|
||||
- PHP-5.6
|
||||
- PHP-7.0
|
||||
|
||||
clone_depth: 64
|
||||
|
||||
skip_commits:
|
||||
files:
|
||||
- docs/*
|
||||
- NEWS
|
||||
- UPGRADING
|
||||
- UPGRADING.INTERNALS
|
||||
|
||||
cache:
|
||||
- c:\build-cache
|
||||
|
||||
environment:
|
||||
PHP_BUILD_CACHE_BASE_DIR: c:\build-cache
|
||||
PHP_BUILD_OBJ_DIR: c:\obj
|
||||
PHP_BUILD_CACHE_SDK_DIR: c:\build-cache\sdk
|
||||
PHP_BUILD_SDK_BRANCH: php-sdk-2.2.0
|
||||
PHP_BUILD_CRT: vs16
|
||||
# ext and env setup for tests
|
||||
#MYSQL_TEST_PASSWD: Password12!
|
||||
#MYSQL_TEST_USER: root
|
||||
#PDO_MYSQL_TEST_DSN: "pgsql:host=127.0.0.1 port=5432 dbname=test user=root password=Password12!"
|
||||
#PDO_MYSQL_TEST_USER: root
|
||||
#PDO_MYSQL_TEST_PASS: Password12!
|
||||
#PGSQL_TEST_CONNSTR: "host=127.0.0.1 dbname=test port=5432 user=postgres password=Password12!"
|
||||
#PDO_PGSQL_TEST_DSN: "pgsql:host=127.0.0.1 port=5432 dbname=test user=postgres password=Password12!"
|
||||
#build permutations
|
||||
matrix:
|
||||
- THREAD_SAFE: 0
|
||||
OPCACHE: 0
|
||||
PARALLEL: -j2
|
||||
- THREAD_SAFE: 1
|
||||
OPCACHE: 1
|
||||
PARALLEL: -j2
|
||||
INTRINSICS: AVX
|
||||
|
||||
services:
|
||||
# the setup scripts have to be touched, once some other db version is used
|
||||
- mysql
|
||||
- postgresql101
|
||||
- mssql2017
|
||||
|
||||
platform:
|
||||
- x64
|
||||
# - x86
|
||||
|
||||
build_script:
|
||||
- appveyor\build.bat
|
||||
|
||||
test_script:
|
||||
- appveyor\test.bat
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
env:
|
||||
CIRRUS_CLONE_DEPTH: 1
|
||||
ARCH: amd64
|
||||
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-0
|
||||
|
||||
task:
|
||||
name: FREEBSD_DEBUG_NTS
|
||||
install_script:
|
||||
#- sed -i -e 's/quarterly/latest/g' /etc/pkg/FreeBSD.conf
|
||||
#- pkg upgrade -y
|
||||
- kldload accf_http
|
||||
- pkg install -y autoconf bison gmake re2c icu libiconv png freetype2 enchant2 bzip2 krb5 t1lib gmp tidyp libsodium libzip libxml2 libxslt openssl oniguruma pkgconf webp
|
||||
script:
|
||||
- ./buildconf -f
|
||||
- ./configure --prefix=/usr/local --enable-debug --enable-option-checking=fatal --enable-fpm --with-pdo-sqlite --without-pear --with-bz2 --with-jpeg --with-webp --with-freetype --enable-gd --enable-exif --with-zip --with-zlib --enable-soap --enable-xmlreader --with-xsl --with-libxml --enable-shmop --enable-pcntl --enable-mbstring --with-curl --enable-sockets --with-openssl --with-iconv=/usr/local --enable-bcmath --enable-calendar --enable-ftp --with-kerberos --with-ffi --enable-zend-test --enable-intl --with-mhash --with-sodium --enable-werror --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d
|
||||
- gmake -j2
|
||||
- mkdir /etc/php.d
|
||||
- gmake install
|
||||
- echo opcache.enable_cli=1 > /etc/php.d/opcache.ini
|
||||
- echo opcache.protect_memory=1 >> /etc/php.d/opcache.ini
|
||||
# Specify opcache.preload_user as we're running as root.
|
||||
- echo opcache.preload_user=root >> /etc/php.d/opcache.ini
|
||||
tests_script:
|
||||
- export SKIP_IO_CAPTURE_TESTS=1
|
||||
- export CI_NO_IPV6=1
|
||||
- sapi/cli/php run-tests.php -P -q -j2 -g FAIL,XFAIL,BORK,WARN,LEAK,XLEAK,SKIP --offline --show-diff --show-slow 1000 --set-timeout 120 -d zend_extension=opcache.so
|
||||
@@ -1,34 +0,0 @@
|
||||
# https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
tab_width = 4
|
||||
|
||||
[{*.{awk,bat,c,cpp,d,h,l,re,skl,w32,y},Makefile*}]
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
|
||||
[*.{dtd,html,inc,php,phpt,rng,wsdl,xml,xsd,xsl}]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
|
||||
[*.{ac,m4,sh,yml}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.md]
|
||||
indent_style = space
|
||||
max_line_length = 80
|
||||
|
||||
[COMMIT_EDITMSG]
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
max_line_length = 80
|
||||
|
||||
[*.patch]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -5,20 +5,20 @@ end
|
||||
document set_ts
|
||||
set the ts resource, it is impossible for gdb to
|
||||
call ts_resource_ex while no process is running,
|
||||
but we could get the resource from the argument
|
||||
but we could get the resource from the argument
|
||||
of frame info.
|
||||
end
|
||||
|
||||
define ____executor_globals
|
||||
if basic_functions_module.zts
|
||||
set $tsrm_ls = _tsrm_ls_cache
|
||||
if !$tsrm_ls
|
||||
set $tsrm_ls = ts_resource_ex(0, 0)
|
||||
end
|
||||
set $eg = ((zend_executor_globals*) (*((void ***) $tsrm_ls))[executor_globals_id-1])
|
||||
set $cg = ((zend_compiler_globals*) (*((void ***) $tsrm_ls))[compiler_globals_id-1])
|
||||
set $eg_ptr = $eg
|
||||
else
|
||||
set $eg = executor_globals
|
||||
set $cg = compiler_globals
|
||||
set $eg_ptr = (zend_executor_globals*) &executor_globals
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,118 +29,129 @@ document ____executor_globals
|
||||
end
|
||||
|
||||
define print_cvs
|
||||
if $argc == 0
|
||||
____executor_globals
|
||||
set $cv_ex_ptr = $eg.current_execute_data
|
||||
else
|
||||
set $cv_ex_ptr = (zend_execute_data *)$arg0
|
||||
____executor_globals
|
||||
set $p = $eg.current_execute_data.CVs
|
||||
set $c = $eg.current_execute_data.op_array.last_var
|
||||
set $v = $eg.current_execute_data.op_array.vars
|
||||
set $i = 0
|
||||
|
||||
printf "Compiled variables count: %d\n", $c
|
||||
while $i < $c
|
||||
printf "%d = %s\n", $i, $v[$i].name
|
||||
if $p[$i] != 0
|
||||
printzv *$p[$i]
|
||||
else
|
||||
printf "*uninitialized*\n"
|
||||
end
|
||||
set $i = $i + 1
|
||||
end
|
||||
set $cv_count = $cv_ex_ptr.func.op_array.last_var
|
||||
set $cv = $cv_ex_ptr.func.op_array.vars
|
||||
set $cv_idx = 0
|
||||
set $callFrameSize = (sizeof(zend_execute_data) + sizeof(zval) - 1) / sizeof(zval)
|
||||
|
||||
printf "Compiled variables count: %d\n\n", $cv_count
|
||||
while $cv_idx < $cv_count
|
||||
printf "[%d] '%s'\n", $cv_idx, $cv[$cv_idx].val
|
||||
set $zvalue = ((zval *) $cv_ex_ptr) + $callFrameSize + $cv_idx
|
||||
printzv $zvalue
|
||||
set $cv_idx = $cv_idx + 1
|
||||
end
|
||||
end
|
||||
|
||||
document print_cvs
|
||||
Prints the compiled variables and their values.
|
||||
If a zend_execute_data pointer is set this will print the compiled
|
||||
variables of that scope. If no parameter is used it will use
|
||||
current_execute_data for scope.
|
||||
|
||||
usage: print_cvs [zend_execute_data *]
|
||||
end
|
||||
|
||||
define dump_bt
|
||||
set $ex = $arg0
|
||||
while $ex
|
||||
printf "[%p] ", $ex
|
||||
set $func = $ex->func
|
||||
if $func
|
||||
if $ex->This->value.obj
|
||||
if $func->common.scope
|
||||
printf "%s->", $func->common.scope->name->val
|
||||
set $t = $arg0
|
||||
while $t
|
||||
printf "[%p] ", $t
|
||||
set $fst = $t->function_state
|
||||
if $fst.function->common.function_name
|
||||
if $fst.arguments
|
||||
set $count = (int)*($fst.arguments)
|
||||
|
||||
if $t->object
|
||||
if $fst.function.common.scope
|
||||
printf "%s->", $fst.function.common.scope->name
|
||||
else
|
||||
if !$eg
|
||||
____executor_globals
|
||||
end
|
||||
|
||||
set $known_class = 0
|
||||
if $eg
|
||||
set $handle = $t->object.value.obj.handle
|
||||
set $handlers = $t->object.value.obj.handlers
|
||||
set $zobj = (zend_object *)$eg.objects_store.object_buckets[$handle].bucket.obj.object
|
||||
|
||||
if $handlers->get_class_entry == &zend_std_object_get_class
|
||||
set $known_class = 1
|
||||
|
||||
if $handlers.get_class_name
|
||||
if $handlers.get_class_name != &zend_std_object_get_class_name
|
||||
set $known_class = 0
|
||||
end
|
||||
end
|
||||
|
||||
if $known_class
|
||||
printf "%s->", $zobj->ce.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if !$known_class
|
||||
printf "(Unknown)->"
|
||||
end
|
||||
end
|
||||
else
|
||||
printf "%s->", $ex->This->value.obj->ce.name->val
|
||||
if $fst.function.common.scope
|
||||
printf "%s::", $fst.function.common.scope->name
|
||||
end
|
||||
end
|
||||
|
||||
printf "%s(", $fst.function->common.function_name
|
||||
while $count > 0
|
||||
set $zvalue = *(zval **)($fst.arguments - $count)
|
||||
set $type = $zvalue->type
|
||||
if $type == 0
|
||||
printf "NULL"
|
||||
end
|
||||
if $type == 1
|
||||
printf "%ld", $zvalue->value.lval
|
||||
end
|
||||
if $type == 2
|
||||
printf "%lf", $zvalue->value.dval
|
||||
end
|
||||
if $type == 3
|
||||
if $zvalue->value.lval
|
||||
printf "true"
|
||||
else
|
||||
printf "false"
|
||||
end
|
||||
end
|
||||
if $type == 4
|
||||
printf "array(%d)[%p]", $zvalue->value.ht->nNumOfElements, $zvalue
|
||||
end
|
||||
if $type == 5
|
||||
printf "object[%p]", $zvalue
|
||||
end
|
||||
if $type == 6
|
||||
____print_str $zvalue->value.str.val $zvalue->value.str.len
|
||||
end
|
||||
if $type == 7
|
||||
printf "resource(#%d)", $zvalue->value.lval
|
||||
end
|
||||
if $type == 8
|
||||
printf "constant"
|
||||
end
|
||||
if $type == 9
|
||||
printf "const_array"
|
||||
end
|
||||
if $type > 9
|
||||
printf "unknown type %d", $type
|
||||
end
|
||||
set $count = $count -1
|
||||
if $count > 0
|
||||
printf ", "
|
||||
end
|
||||
end
|
||||
printf ") "
|
||||
else
|
||||
if $func->common.scope
|
||||
printf "%s::", $func->common.scope->name->val
|
||||
end
|
||||
printf "%s() ", $fst.function->common.function_name
|
||||
end
|
||||
|
||||
if $func->common.function_name
|
||||
printf "%s(", $func->common.function_name->val
|
||||
else
|
||||
printf "(main"
|
||||
end
|
||||
|
||||
set $callFrameSize = (sizeof(zend_execute_data) + sizeof(zval) - 1) / sizeof(zval)
|
||||
|
||||
set $count = $ex->This.u2.num_args
|
||||
set $arg = 0
|
||||
while $arg < $count
|
||||
if $arg > 0
|
||||
printf ", "
|
||||
end
|
||||
|
||||
set $zvalue = (zval *) $ex + $callFrameSize + $arg
|
||||
set $type = $zvalue->u1.v.type
|
||||
if $type == 1
|
||||
printf "NULL"
|
||||
end
|
||||
if $type == 2
|
||||
printf "false"
|
||||
end
|
||||
if $type == 3
|
||||
printf "true"
|
||||
end
|
||||
if $type == 4
|
||||
printf "%ld", $zvalue->value.lval
|
||||
end
|
||||
if $type == 5
|
||||
printf "%f", $zvalue->value.dval
|
||||
end
|
||||
if $type == 6
|
||||
____print_str $zvalue->value.str->val $zvalue->value.str->len
|
||||
end
|
||||
if $type == 7
|
||||
printf "array(%d)[%p]", $zvalue->value.arr->nNumOfElements, $zvalue
|
||||
end
|
||||
if $type == 8
|
||||
printf "object[%p]", $zvalue
|
||||
end
|
||||
if $type == 9
|
||||
printf "resource(#%d)", $zvalue->value.lval
|
||||
end
|
||||
if $type == 10
|
||||
printf "reference"
|
||||
end
|
||||
if $type > 10
|
||||
printf "unknown type %d", $type
|
||||
end
|
||||
set $arg = $arg + 1
|
||||
end
|
||||
|
||||
printf ") "
|
||||
else
|
||||
printf "??? "
|
||||
end
|
||||
if $func != 0
|
||||
if $func->type == 2
|
||||
printf "%s:%d ", $func->op_array.filename->val, $ex->opline->lineno
|
||||
else
|
||||
printf "[internal function]"
|
||||
end
|
||||
if $t->op_array != 0
|
||||
printf "%s:%d ", $t->op_array->filename, $t->opline->lineno
|
||||
end
|
||||
set $ex = $ex->prev_execute_data
|
||||
set $t = $t->prev_execute_data
|
||||
printf "\n"
|
||||
end
|
||||
end
|
||||
@@ -151,7 +162,7 @@ end
|
||||
|
||||
define printzv
|
||||
set $ind = 1
|
||||
____printzv $arg0 0
|
||||
____printzv $arg0 0
|
||||
end
|
||||
|
||||
document printzv
|
||||
@@ -160,61 +171,68 @@ end
|
||||
|
||||
define ____printzv_contents
|
||||
set $zvalue = $arg0
|
||||
set $type = $zvalue->u1.v.type
|
||||
set $type = $zvalue->type
|
||||
|
||||
# 15 == IS_INDIRECT
|
||||
if $type > 5 && $type < 12
|
||||
printf "(refcount=%d) ", $zvalue->value.counted->gc.refcount
|
||||
printf "(refcount=%d", $zvalue->refcount__gc
|
||||
if $zvalue->is_ref__gc
|
||||
printf ",is_ref"
|
||||
end
|
||||
|
||||
printf ") "
|
||||
if $type == 0
|
||||
printf "UNDEF"
|
||||
end
|
||||
if $type == 1
|
||||
printf "NULL"
|
||||
end
|
||||
if $type == 2
|
||||
printf "bool: false"
|
||||
end
|
||||
if $type == 3
|
||||
printf "bool: true"
|
||||
end
|
||||
if $type == 4
|
||||
if $type == 1
|
||||
printf "long: %ld", $zvalue->value.lval
|
||||
end
|
||||
if $type == 5
|
||||
printf "double: %f", $zvalue->value.dval
|
||||
if $type == 2
|
||||
printf "double: %lf", $zvalue->value.dval
|
||||
end
|
||||
if $type == 6
|
||||
printf "string: %s", $zvalue->value.str->val
|
||||
if $type == 3
|
||||
printf "bool: "
|
||||
if $zvalue->value.lval
|
||||
printf "true"
|
||||
else
|
||||
printf "false"
|
||||
end
|
||||
end
|
||||
if $type == 7
|
||||
printf "array: "
|
||||
if $type == 4
|
||||
printf "array(%d): ", $zvalue->value.ht->nNumOfElements
|
||||
if ! $arg1
|
||||
printf "{\n"
|
||||
set $ind = $ind + 1
|
||||
____print_ht $zvalue->value.arr 1
|
||||
____print_ht $zvalue->value.ht 1
|
||||
set $ind = $ind - 1
|
||||
set $i = $ind
|
||||
while $i > 0
|
||||
printf " "
|
||||
set $i = $i - 1
|
||||
end
|
||||
printf "}"
|
||||
end
|
||||
set $type = 0
|
||||
end
|
||||
if $type == 8
|
||||
if $type == 5
|
||||
printf "object"
|
||||
____executor_globals
|
||||
set $handle = $zvalue->value.obj.handle
|
||||
set $handlers = $zvalue->value.obj.handlers
|
||||
set $zobj = $zvalue->value.obj
|
||||
set $cname = $zobj->ce->name->val
|
||||
if basic_functions_module.zts
|
||||
set $zobj = zend_objects_get_address($zvalue, $tsrm_ls)
|
||||
else
|
||||
set $zobj = zend_objects_get_address($zvalue)
|
||||
end
|
||||
if $handlers->get_class_entry == &zend_std_object_get_class
|
||||
set $cname = $zobj->ce.name
|
||||
else
|
||||
set $cname = "Unknown"
|
||||
end
|
||||
printf "(%s) #%d", $cname, $handle
|
||||
if ! $arg1
|
||||
if $handlers->get_properties == &zend_std_get_properties
|
||||
if $zobj->properties
|
||||
printf "\nProperties "
|
||||
set $ht = $zobj->properties
|
||||
set $ht = $zobj->properties
|
||||
if $ht
|
||||
printf "(%d): ", $ht->nNumOfElements
|
||||
printf "{\n"
|
||||
set $ind = $ind + 1
|
||||
____print_ht $ht 1
|
||||
set $ind = $ind - 1
|
||||
@@ -223,52 +241,28 @@ define ____printzv_contents
|
||||
printf " "
|
||||
set $i = $i - 1
|
||||
end
|
||||
printf "}"
|
||||
else
|
||||
printf " {\n"
|
||||
set $ht = &$zobj->ce->properties_info
|
||||
set $k = 0
|
||||
set $num = $ht->nNumUsed
|
||||
while $k < $num
|
||||
set $p = (Bucket*)($ht->arData + $k)
|
||||
set $name = $p->key
|
||||
set $prop = (zend_property_info*)$p->val.value.ptr
|
||||
set $val = (zval*)((char*)$zobj + $prop->offset)
|
||||
printf "%s => ", $name->val
|
||||
printzv $val
|
||||
set $k = $k + 1
|
||||
end
|
||||
echo "no properties found"
|
||||
end
|
||||
end
|
||||
end
|
||||
set $type = 0
|
||||
end
|
||||
if $type == 6
|
||||
printf "string(%d): ", $zvalue->value.str.len
|
||||
____print_str $zvalue->value.str.val $zvalue->value.str.len
|
||||
end
|
||||
if $type == 7
|
||||
printf "resource: #%d", $zvalue->value.lval
|
||||
end
|
||||
if $type == 8
|
||||
printf "constant"
|
||||
end
|
||||
if $type == 9
|
||||
printf "resource: #%d", $zvalue->value.res->handle
|
||||
printf "const_array"
|
||||
end
|
||||
if $type == 10
|
||||
printf "reference: "
|
||||
____printzv &$zvalue->value.ref->val $arg1
|
||||
end
|
||||
if $type == 11
|
||||
printf "CONSTANT_AST"
|
||||
end
|
||||
if $type == 12
|
||||
printf "indirect: "
|
||||
____printzv $zvalue->value.zv $arg1
|
||||
end
|
||||
if $type == 13
|
||||
printf "pointer: %p", $zvalue->value.ptr
|
||||
end
|
||||
if $type == 15
|
||||
printf "_ERROR"
|
||||
end
|
||||
if $type == 16
|
||||
printf "_BOOL"
|
||||
end
|
||||
if $type == 17
|
||||
printf "_NUMBER"
|
||||
end
|
||||
if $type > 17
|
||||
if $type > 9
|
||||
printf "unknown type %d", $type
|
||||
end
|
||||
printf "\n"
|
||||
@@ -280,97 +274,88 @@ define ____printzv
|
||||
|
||||
printf "[%p] ", $zvalue
|
||||
|
||||
if $zvalue == $eg.uninitialized_zval_ptr
|
||||
printf "*uninitialized* "
|
||||
end
|
||||
|
||||
set $zcontents = (zval*) $zvalue
|
||||
if $arg1
|
||||
____printzv_contents $zcontents $arg1
|
||||
else
|
||||
____printzv_contents $zcontents 0
|
||||
____printzv_contents $zcontents 0
|
||||
end
|
||||
end
|
||||
|
||||
define print_global_vars
|
||||
____executor_globals
|
||||
set $symtable = ((HashTable *)&($eg_ptr->symbol_table))
|
||||
print_ht $symtable
|
||||
end
|
||||
define ____print_const_table
|
||||
set $ht = $arg0
|
||||
set $p = $ht->pListHead
|
||||
|
||||
document print_global_vars
|
||||
Prints the global variables
|
||||
while $p != 0
|
||||
set $const = (zend_constant *) $p->pData
|
||||
|
||||
set $i = $ind
|
||||
while $i > 0
|
||||
printf " "
|
||||
set $i = $i - 1
|
||||
end
|
||||
|
||||
if $p->nKeyLength > 0
|
||||
____print_str $p->arKey $p->nKeyLength
|
||||
printf " => "
|
||||
else
|
||||
printf "%d => ", $p->h
|
||||
end
|
||||
|
||||
____printzv_contents &$const->value 0
|
||||
set $p = $p->pListNext
|
||||
end
|
||||
end
|
||||
|
||||
define print_const_table
|
||||
set $ind = 1
|
||||
printf "[%p] {\n", $arg0
|
||||
____print_ht $arg0 4
|
||||
____print_const_table $arg0
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
document print_const_table
|
||||
Dumps elements of Constants HashTable
|
||||
Example: print_const_table executor_globals.zend_constants
|
||||
end
|
||||
|
||||
define ____print_ht
|
||||
set $ht = (HashTable*)$arg0
|
||||
set $n = $ind
|
||||
while $n > 0
|
||||
printf " "
|
||||
set $n = $n - 1
|
||||
end
|
||||
set $p = $ht->pListHead
|
||||
|
||||
if $ht->u.v.flags & 4
|
||||
printf "Packed"
|
||||
else
|
||||
printf "Hash"
|
||||
end
|
||||
printf "(%d)[%p]: {\n", $ht->nNumOfElements, $ht
|
||||
|
||||
set $num = $ht->nNumUsed
|
||||
set $i = 0
|
||||
set $ind = $ind + 1
|
||||
while $i < $num
|
||||
set $p = (Bucket*)($ht->arData + $i)
|
||||
set $n = $ind
|
||||
if $p->val.u1.v.type > 0
|
||||
while $n > 0
|
||||
printf " "
|
||||
set $n = $n - 1
|
||||
end
|
||||
printf "[%d] ", $i
|
||||
if $p->key
|
||||
____print_str $p->key->val $p->key->len
|
||||
printf " => "
|
||||
else
|
||||
printf "%d => ", $p->h
|
||||
end
|
||||
if $arg1 == 0
|
||||
printf "%p\n", (zval *)&$p->val
|
||||
end
|
||||
if $arg1 == 1
|
||||
set $zval = (zval *)&$p->val
|
||||
____printzv $zval 1
|
||||
end
|
||||
if $arg1 == 2
|
||||
printf "%s\n", (char*)$p->val.value.ptr
|
||||
end
|
||||
if $arg1 == 3
|
||||
set $func = (zend_function*)$p->val.value.ptr
|
||||
printf "\"%s\"\n", $func->common.function_name->val
|
||||
end
|
||||
if $arg1 == 4
|
||||
set $const = (zend_constant *)$p->val.value.ptr
|
||||
____printzv $const 1
|
||||
end
|
||||
while $p != 0
|
||||
set $i = $ind
|
||||
while $i > 0
|
||||
printf " "
|
||||
set $i = $i - 1
|
||||
end
|
||||
set $i = $i + 1
|
||||
|
||||
if $p->nKeyLength > 0
|
||||
____print_str $p->arKey $p->nKeyLength
|
||||
printf " => "
|
||||
else
|
||||
printf "%d => ", $p->h
|
||||
end
|
||||
|
||||
if $arg1 == 0
|
||||
printf "%p\n", (void*)$p->pData
|
||||
end
|
||||
if $arg1 == 1
|
||||
set $zval = *(zval **)$p->pData
|
||||
____printzv $zval 1
|
||||
end
|
||||
if $arg1 == 2
|
||||
printf "%s\n", (char*)$p->pData
|
||||
end
|
||||
|
||||
set $p = $p->pListNext
|
||||
end
|
||||
set $ind = $ind - 1
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
define print_ht
|
||||
set $ind = 0
|
||||
set $ind = 1
|
||||
printf "[%p] {\n", $arg0
|
||||
____print_ht $arg0 1
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
document print_ht
|
||||
@@ -378,8 +363,10 @@ document print_ht
|
||||
end
|
||||
|
||||
define print_htptr
|
||||
set $ind = 0
|
||||
set $ind = 1
|
||||
printf "[%p] {\n", $arg0
|
||||
____print_ht $arg0 0
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
document print_htptr
|
||||
@@ -387,17 +374,46 @@ document print_htptr
|
||||
end
|
||||
|
||||
define print_htstr
|
||||
set $ind = 0
|
||||
set $ind = 1
|
||||
printf "[%p] {\n", $arg0
|
||||
____print_ht $arg0 2
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
document print_htstr
|
||||
dumps elements of HashTable made of strings
|
||||
end
|
||||
|
||||
define ____print_ft
|
||||
set $ht = $arg0
|
||||
set $p = $ht->pListHead
|
||||
|
||||
while $p != 0
|
||||
set $func = (zend_function*)$p->pData
|
||||
|
||||
set $i = $ind
|
||||
while $i > 0
|
||||
printf " "
|
||||
set $i = $i - 1
|
||||
end
|
||||
|
||||
if $p->nKeyLength > 0
|
||||
____print_str $p->arKey $p->nKeyLength
|
||||
printf " => "
|
||||
else
|
||||
printf "%d => ", $p->h
|
||||
end
|
||||
|
||||
printf "\"%s\"\n", $func->common.function_name
|
||||
set $p = $p->pListNext
|
||||
end
|
||||
end
|
||||
|
||||
define print_ft
|
||||
set $ind = 0
|
||||
____print_ht $arg0 3
|
||||
set $ind = 1
|
||||
printf "[%p] {\n", $arg0
|
||||
____print_ft $arg0
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
document print_ft
|
||||
@@ -413,15 +429,15 @@ define ____print_inh_class
|
||||
printf "final "
|
||||
end
|
||||
end
|
||||
printf "class %s", $ce->name->val
|
||||
printf "class %s", $ce->name
|
||||
if $ce->parent != 0
|
||||
printf " extends %s", $ce->parent->name->val
|
||||
printf " extends %s", $ce->parent->name
|
||||
end
|
||||
if $ce->num_interfaces != 0
|
||||
printf " implements"
|
||||
set $tmp = 0
|
||||
while $tmp < $ce->num_interfaces
|
||||
printf " %s", $ce->interfaces[$tmp]->name->val
|
||||
printf " %s", $ce->interfaces[$tmp]->name
|
||||
set $tmp = $tmp + 1
|
||||
if $tmp < $ce->num_interfaces
|
||||
printf ","
|
||||
@@ -433,10 +449,10 @@ end
|
||||
|
||||
define ____print_inh_iface
|
||||
set $ce = $arg0
|
||||
printf "interface %s", $ce->name->val
|
||||
printf "interface %s", $ce->name
|
||||
if $ce->num_interfaces != 0
|
||||
set $ce = $ce->interfaces[0]
|
||||
printf " extends %s", $ce->name->val
|
||||
printf " extends %s", $ce->name
|
||||
else
|
||||
set $ce = 0
|
||||
end
|
||||
@@ -471,13 +487,10 @@ define print_inh
|
||||
end
|
||||
|
||||
define print_pi
|
||||
set $pi = (zend_property_info *)$arg0
|
||||
set $initial_offset = ((uint32_t)(zend_uintptr_t)(&((zend_object*)0)->properties_table[(0)]))
|
||||
set $ptr_to_val = (zval*)((char*)$pi->ce->default_properties_table + $pi->offset - $initial_offset)
|
||||
set $pi = $arg0
|
||||
printf "[%p] {\n", $pi
|
||||
printf " offset = %p\n", $pi->offset
|
||||
printf " ce = [%p] %s\n", $pi->ce, $pi->ce->name->val
|
||||
printf " flags = 0x%x (", $pi->flags
|
||||
printf " h = %lu\n", $pi->h
|
||||
printf " flags = %d (", $pi->flags
|
||||
if $pi->flags & 0x100
|
||||
printf "ZEND_ACC_PUBLIC"
|
||||
else
|
||||
@@ -488,49 +501,29 @@ define print_pi
|
||||
printf "ZEND_ACC_PRIVATE"
|
||||
else
|
||||
if $pi->flags & 0x800
|
||||
printf "ZEND_ACC_EARLY_BINDING"
|
||||
else
|
||||
if $pi->flags & 0x20000
|
||||
printf "ZEND_ACC_SHADOW"
|
||||
end
|
||||
printf "ZEND_ACC_CHANGED"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
printf ")\n"
|
||||
printf " name = "
|
||||
print_zstr $pi->name
|
||||
printf " default value: "
|
||||
printzv $ptr_to_val
|
||||
printf "}\n"
|
||||
end
|
||||
|
||||
document print_pi
|
||||
Takes a pointer to an object's property and prints the property information
|
||||
usage: print_pi <ptr>
|
||||
____print_str $pi->name $pi->name_length
|
||||
printf "\n}\n"
|
||||
end
|
||||
|
||||
define ____print_str
|
||||
set $tmp = 0
|
||||
set $str = $arg0
|
||||
if $argc > 2
|
||||
set $maxlen = $arg2
|
||||
else
|
||||
set $maxlen = 256
|
||||
end
|
||||
|
||||
printf "\""
|
||||
while $tmp < $arg1 && $tmp < $maxlen
|
||||
if $str[$tmp] > 31 && $str[$tmp] < 127
|
||||
while $tmp < $arg1
|
||||
if $str[$tmp] > 32 && $str[$tmp] < 127
|
||||
printf "%c", $str[$tmp]
|
||||
else
|
||||
printf "\\%o", $str[$tmp]
|
||||
end
|
||||
set $tmp = $tmp + 1
|
||||
end
|
||||
if $tmp != $arg1
|
||||
printf "..."
|
||||
end
|
||||
printf "\""
|
||||
end
|
||||
|
||||
@@ -541,10 +534,10 @@ define printzn
|
||||
if $znode->op_type == 1
|
||||
set $optype = "IS_CONST"
|
||||
end
|
||||
if $znode->op_type == 2
|
||||
if $znode->op_type == 2
|
||||
set $optype = "IS_TMP_VAR"
|
||||
end
|
||||
if $znode->op_type == 4
|
||||
if $znode->op_type == 4
|
||||
set $optype = "IS_VAR"
|
||||
end
|
||||
if $znode->op_type == 8
|
||||
@@ -574,11 +567,11 @@ end
|
||||
|
||||
document printzn
|
||||
print type and content of znode.
|
||||
usage: printzn &opline->op1
|
||||
usage: printzn &opline->op1
|
||||
end
|
||||
|
||||
define printzops
|
||||
printf "op1 => "
|
||||
printf "op1 => "
|
||||
printzn &execute_data->opline.op1
|
||||
printf "op2 => "
|
||||
printzn &execute_data->opline.op2
|
||||
@@ -590,23 +583,6 @@ document printzops
|
||||
dump operands of the current opline
|
||||
end
|
||||
|
||||
define print_zstr
|
||||
set $zstr = (zend_string *)$arg0
|
||||
if $argc == 2
|
||||
set $maxlen = $arg1
|
||||
else
|
||||
set $maxlen = $zstr->len
|
||||
end
|
||||
printf "string(%d) ", $zstr->len
|
||||
____print_str $zstr->val $zstr->len $maxlen
|
||||
printf "\n"
|
||||
end
|
||||
|
||||
document print_zstr
|
||||
print the length and contents of a zend string
|
||||
usage: print_zstr <ptr> [max length]
|
||||
end
|
||||
|
||||
define zbacktrace
|
||||
____executor_globals
|
||||
dump_bt $eg.current_execute_data
|
||||
@@ -619,27 +595,75 @@ document zbacktrace
|
||||
> (gdb) dump_bt $eg.current_execute_data
|
||||
end
|
||||
|
||||
define lookup_root
|
||||
set $found = 0
|
||||
if gc_globals->roots
|
||||
set $current = gc_globals->roots->next
|
||||
printf "looking ref %p in roots\n", $arg0
|
||||
while $current != &gc_globals->roots
|
||||
if $current->ref == $arg0
|
||||
set $found = $current
|
||||
break
|
||||
define zmemcheck
|
||||
set $p = alloc_globals.head
|
||||
set $stat = "?"
|
||||
set $total_size = 0
|
||||
if $arg0 != 0
|
||||
set $not_found = 1
|
||||
else
|
||||
set $not_found = 0
|
||||
end
|
||||
printf " block size status file:line\n"
|
||||
printf "-------------------------------------------------------------------------------\n"
|
||||
while $p
|
||||
set $aptr = $p + sizeof(struct _zend_mem_header) + sizeof(align_test)
|
||||
if $arg0 == 0 || (void *)$aptr == (void *)$arg0
|
||||
if $p->magic == 0x7312f8dc
|
||||
set $stat = "OK"
|
||||
end
|
||||
if $p->magic == 0x99954317
|
||||
set $stat = "FREED"
|
||||
end
|
||||
if $p->magic == 0xfb8277dc
|
||||
set $stat = "CACHED"
|
||||
end
|
||||
set $filename = strrchr($p->filename, '/')
|
||||
if !$filename
|
||||
set $filename = $p->filename
|
||||
else
|
||||
set $filename = $filename + 1
|
||||
end
|
||||
printf " %p ", $aptr
|
||||
if $p->size == sizeof(struct _zval_struct) && ((struct _zval_struct *)$aptr)->type >= 0 && ((struct _zval_struct *)$aptr)->type < 10
|
||||
printf "ZVAL?(%-2d) ", $p->size
|
||||
else
|
||||
printf "%-9d ", $p->size
|
||||
end
|
||||
set $total_size = $total_size + $p->size
|
||||
printf "%-06s %s:%d", $stat, $filename, $p->lineno
|
||||
if $p->orig_filename
|
||||
set $orig_filename = strrchr($p->orig_filename, '/')
|
||||
if !$orig_filename
|
||||
set $orig_filename = $p->orig_filename
|
||||
else
|
||||
set $orig_filename = $orig_filename + 1
|
||||
end
|
||||
printf " <= %s:%d\n", $orig_filename, $p->orig_lineno
|
||||
else
|
||||
printf "\n"
|
||||
end
|
||||
if $arg0 != 0
|
||||
set $p = 0
|
||||
set $not_found = 0
|
||||
else
|
||||
set $p = $p->pNext
|
||||
end
|
||||
set $current = $current->next
|
||||
end
|
||||
if $found != 0
|
||||
printf "found root %p\n", $found
|
||||
else
|
||||
printf "not found\n"
|
||||
set $p = $p->pNext
|
||||
end
|
||||
end
|
||||
if $not_found
|
||||
printf "no such block that begins at %p.\n", $aptr
|
||||
end
|
||||
if $arg0 == 0
|
||||
printf "-------------------------------------------------------------------------------\n"
|
||||
printf " total: %d bytes\n", $total_size
|
||||
end
|
||||
end
|
||||
|
||||
document lookup_root
|
||||
lookup a refcounted in root
|
||||
usage: lookup_root [ptr].
|
||||
document zmemcheck
|
||||
show status of a memory block.
|
||||
usage: zmemcheck [ptr].
|
||||
if ptr is 0, all blocks will be listed.
|
||||
end
|
||||
+175
-19
@@ -1,19 +1,175 @@
|
||||
# Replace $Id$ strings with 40-character hexadecimal blob object name.
|
||||
/ext/ext_skel.php ident
|
||||
/ext/phar/phar/pharcommand.inc ident
|
||||
/ext/dba/libinifile/inifile.c ident
|
||||
/ext/dba/libflatfile/flatfile.c ident
|
||||
/ext/dba/libcdb/cdb_make.c ident
|
||||
/ext/dba/libcdb/cdb.c ident
|
||||
/run-tests.php ident
|
||||
|
||||
# A custom merge driver to avoid automatic merges. These are manually merged.
|
||||
# See https://wiki.php.net/vcs/gitfaq
|
||||
/NEWS merge=NEWS
|
||||
/UPGRADING merge=NEWS
|
||||
/UPGRADING.INTERNALS merge=NEWS
|
||||
|
||||
# Enable commit diffs for binary PHP test files. Some PHP test files include
|
||||
# special characters, such as ASCII control characters. Git recognizes these as
|
||||
# binary and wouldn't generate diffs.
|
||||
*.phpt diff
|
||||
ext/mysqlnd/mysqlnd.h ident
|
||||
ext/simplexml/simplexml.c ident
|
||||
ext/iconv/php_iconv.h ident
|
||||
ext/posix/posix.c ident
|
||||
ext/recode/recode.c ident
|
||||
ext/skeleton/create_stubs ident
|
||||
ext/phar/phar/pharcommand.inc ident
|
||||
ext/phar/phar.c ident
|
||||
ext/sysvmsg/sysvmsg.c ident
|
||||
ext/enchant/enchant.c ident
|
||||
ext/reflection/php_reflection.c ident
|
||||
ext/oci8/oci8.c ident
|
||||
ext/dba/libinifile/inifile.c ident
|
||||
ext/dba/libflatfile/flatfile.c ident
|
||||
ext/dba/libcdb/cdb_make.c ident
|
||||
ext/dba/libcdb/cdb.c ident
|
||||
ext/filter/filter.c ident
|
||||
ext/zip/php_zip.c ident
|
||||
README.input_filter ident
|
||||
run-tests.php ident
|
||||
sapi/nsapi/nsapi.c ident
|
||||
sapi/continuity/capi.c ident
|
||||
Zend/RFCs/002.txt ident
|
||||
Zend/RFCs/003.txt ident
|
||||
NEWS merge=NEWS
|
||||
/ext/bz2/tests/with_strings.phpt -crlf
|
||||
/ext/dom/tests/bug40836.phpt -crlf
|
||||
/ext/dom/tests/domelement.phpt -crlf
|
||||
/ext/ereg/tests/eregi_basic_002.phpt -crlf
|
||||
/ext/iconv/tests/iconv004.phpt -crlf
|
||||
/ext/iconv/tests/iconv_basic.phpt -crlf
|
||||
/ext/iconv/tests/iconv_strpos.phpt -crlf
|
||||
/ext/iconv/tests/iconv_strpos_variation2.phpt -crlf
|
||||
/ext/mbstring/tests/mb_strtoupper_error2.phpt -crlf
|
||||
/ext/phar/tests/delete_in_phar_confirm.phpt -crlf
|
||||
/ext/phar/tests/frontcontroller12.phpt -crlf
|
||||
/ext/phar/tests/security.phpt -crlf
|
||||
/ext/phar/tests/test_signaturealgos.phpt -crlf
|
||||
/ext/reflection/tests/ReflectionMethod_invokeArgs_basic.phpt -crlf
|
||||
/ext/reflection/tests/ReflectionProperty_getModifiers_basic.phpt -crlf
|
||||
/ext/spl/tests/dllist_007.phpt -crlf
|
||||
/ext/spl/tests/iterator_012.phpt -crlf
|
||||
/ext/spl/tests/SplArray_fromArray.phpt -crlf
|
||||
/ext/standard/tests/dir/scandir_variation3.phpt -crlf
|
||||
/ext/standard/tests/general_functions/escapeshellcmd-win32.phpt -crlf
|
||||
/ext/standard/tests/general_functions/set_magic_quotes_runtime_error.phpt -crlf
|
||||
/ext/standard/tests/strings/bug26817.phpt -crlf
|
||||
/ext/standard/tests/strings/bug26973.phpt -crlf
|
||||
/ext/standard/tests/strings/bug27457.phpt -crlf
|
||||
/ext/standard/tests/strings/bug28386.phpt -crlf
|
||||
/ext/standard/tests/strings/bug37262.phpt -crlf
|
||||
/ext/standard/tests/strings/bug40637.phpt -crlf
|
||||
/ext/standard/tests/strings/bug40915.phpt -crlf
|
||||
/ext/standard/tests/strings/bug61374.phpt -crlf
|
||||
/ext/standard/tests/strings/chop_error.phpt -crlf
|
||||
/ext/standard/tests/strings/chop_variation2.phpt -crlf
|
||||
/ext/standard/tests/strings/chunk_split_variation10.phpt -crlf
|
||||
/ext/standard/tests/strings/chunk_split_variation8.phpt -crlf
|
||||
/ext/standard/tests/strings/count_chars_variation2.phpt -crlf
|
||||
/ext/standard/tests/strings/dirname_error.phpt -crlf
|
||||
/ext/standard/tests/strings/fprintf_variation_007_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/highlight_file.phpt -crlf
|
||||
/ext/standard/tests/strings/htmlentities03.phpt -crlf
|
||||
/ext/standard/tests/strings/htmlentities04.phpt -crlf
|
||||
/ext/standard/tests/strings/htmlentities08.phpt -crlf
|
||||
/ext/standard/tests/strings/htmlentities15.phpt -crlf
|
||||
/ext/standard/tests/strings/http_build_query.phpt -crlf
|
||||
/ext/standard/tests/strings/metaphone.phpt -crlf
|
||||
/ext/standard/tests/strings/ord_error.phpt -crlf
|
||||
/ext/standard/tests/strings/printf_basic2.phpt -crlf
|
||||
/ext/standard/tests/strings/printf_variation2.phpt -crlf
|
||||
/ext/standard/tests/strings/quoted_printable_decode_basic.phpt -crlf
|
||||
/ext/standard/tests/strings/rtrim.phpt -crlf
|
||||
/ext/standard/tests/strings/setlocale_variation1.phpt -crlf
|
||||
/ext/standard/tests/strings/sha1_basic.phpt -crlf
|
||||
/ext/standard/tests/strings/similar_text_basic.phpt -crlf
|
||||
/ext/standard/tests/strings/soundex.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_basic1.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_basic4.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_basic7.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_variation12.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_variation28.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_variation29.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_variation30.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_variation31.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_variation38.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_variation42.phpt -crlf
|
||||
/ext/standard/tests/strings/sprintf_variation6.phpt -crlf
|
||||
/ext/standard/tests/strings/sscanf_basic2.phpt -crlf
|
||||
/ext/standard/tests/strings/sscanf_basic3.phpt -crlf
|
||||
/ext/standard/tests/strings/str_getcsv_001.phpt -crlf
|
||||
/ext/standard/tests/strings/stripcslashes_basic.phpt -crlf
|
||||
/ext/standard/tests/strings/stripos_variation11.phpt -crlf
|
||||
/ext/standard/tests/strings/stripos_variation13.phpt -crlf
|
||||
/ext/standard/tests/strings/stripslashes_variation1.phpt -crlf
|
||||
/ext/standard/tests/strings/stripslashes_variation2.phpt -crlf
|
||||
/ext/standard/tests/strings/stristr_error.phpt -crlf
|
||||
/ext/standard/tests/strings/strnatcasecmp_basic.phpt -crlf
|
||||
/ext/standard/tests/strings/strncasecmp_variation6.phpt -crlf
|
||||
/ext/standard/tests/strings/strncasecmp_variation7.phpt -crlf
|
||||
/ext/standard/tests/strings/strncasecmp_variation8.phpt -crlf
|
||||
/ext/standard/tests/strings/strrchr_error.phpt -crlf
|
||||
/ext/standard/tests/strings/strrchr.phpt -crlf
|
||||
/ext/standard/tests/strings/strrchr_variation11.phpt -crlf
|
||||
/ext/standard/tests/strings/strrpos_error.phpt -crlf
|
||||
/ext/standard/tests/strings/strrpos_variation4.phpt -crlf
|
||||
/ext/standard/tests/strings/strrpos_variation8.phpt -crlf
|
||||
/ext/standard/tests/strings/strstr2.phpt -crlf
|
||||
/ext/standard/tests/strings/strtok_error.phpt -crlf
|
||||
/ext/standard/tests/strings/strtok_variation2.phpt -crlf
|
||||
/ext/standard/tests/strings/strtolower-win32.phpt -crlf
|
||||
/ext/standard/tests/strings/substr_compare.phpt -crlf
|
||||
/ext/standard/tests/strings/ucwords_variation1.phpt -crlf
|
||||
/ext/standard/tests/strings/unpack.phpt -crlf
|
||||
/ext/standard/tests/strings/vfprintf_basic7_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vfprintf_variation11_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vfprintf_variation12_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vfprintf_variation12.phpt -crlf
|
||||
/ext/standard/tests/strings/vfprintf_variation13_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vfprintf_variation7.phpt -crlf
|
||||
/ext/standard/tests/strings/vprintf_basic2.phpt -crlf
|
||||
/ext/standard/tests/strings/vprintf_basic7_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vprintf_variation10.phpt -crlf
|
||||
/ext/standard/tests/strings/vprintf_variation14_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vprintf_variation17.phpt -crlf
|
||||
/ext/standard/tests/strings/vprintf_variation4.phpt -crlf
|
||||
/ext/standard/tests/strings/vsprintf_basic4.phpt -crlf
|
||||
/ext/standard/tests/strings/vsprintf_basic8.phpt -crlf
|
||||
/ext/standard/tests/strings/vsprintf_variation12_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vsprintf_variation13_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vsprintf_variation15_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vsprintf_variation17.phpt -crlf
|
||||
/ext/standard/tests/strings/vsprintf_variation4_64bit.phpt -crlf
|
||||
/ext/standard/tests/strings/vsprintf_variation7.phpt -crlf
|
||||
/ext/standard/tests/strings/wordwrap.phpt -crlf
|
||||
/ext/standard/tests/strings/wordwrap_variation5.phpt -crlf
|
||||
/ext/standard/tests/url/rawurldecode_variation_001.phpt -crlf
|
||||
/ext/tidy/tests/009.phpt -crlf
|
||||
/ext/tidy/tests/013.phpt -crlf
|
||||
/ext/tidy/tests/021.phpt -crlf
|
||||
/ext/tidy/tests/tidy_error.phpt -crlf
|
||||
/ext/tokenizer/tests/002.phpt -crlf
|
||||
/ext/tokenizer/tests/bug26463.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_error.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation11.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation12.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation13.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation14.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation15.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation18.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation19.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation1.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation2.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation3.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation5.phpt -crlf
|
||||
/ext/tokenizer/tests/token_get_all_variation8.phpt -crlf
|
||||
/ext/xml/tests/bug32001b.phpt -crlf
|
||||
/ext/xmlwriter/tests/OO_003.phpt -crlf
|
||||
/ext/xmlwriter/tests/xmlwriter_write_attribute_ns_error_001.phpt -crlf
|
||||
/ext/zlib/tests/008.phpt -crlf
|
||||
/ext/zlib/tests/gzopen_variation4.phpt -crlf
|
||||
/ext/zlib/tests/gzrewind_error.phpt -crlf
|
||||
/ext/zlib/tests/readgzfile_variation7.phpt -crlf
|
||||
/ext/zlib/tests/readgzfile_variation8.phpt -crlf
|
||||
/ext/zlib/tests/zlib_scheme_stat_basic.phpt -crlf
|
||||
/sapi/cli/tests/006.phpt -crlf
|
||||
/tests/run-test/test009.phpt -crlf
|
||||
/Zend/tests/012.phpt -crlf
|
||||
/Zend/tests/bug28072.phpt -crlf
|
||||
/Zend/tests/bug38624.phpt -crlf
|
||||
/Zend/tests/bug40784.phpt -crlf
|
||||
/Zend/tests/bug43053.phpt -crlf
|
||||
/Zend/tests/bug51176.phpt -crlf
|
||||
/Zend/tests/each_003.phpt -crlf
|
||||
/Zend/tests/errmsg_006.phpt -crlf
|
||||
+241
-273
@@ -1,288 +1,256 @@
|
||||
# These files are generated during building or development and are intentionally
|
||||
# untracked to ignore by Git. For other development environment specific files,
|
||||
# such as editor configuration, a good practice is to exclude them using the
|
||||
# .git/info/exclude in the cloned repository or a global .gitignore file.
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Common files ignored across the repository
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Backup copies created by various editors or development tools
|
||||
# General Ignores
|
||||
*~
|
||||
|
||||
# Patches downloaded to patch files or to apply open source contributions
|
||||
*.patch
|
||||
|
||||
# Swap files created by editors and tools to indicate a locked file
|
||||
*.swp
|
||||
|
||||
# Various temporary generated files
|
||||
*.tmp
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Generated by the PHP build system
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Archive library containing .o files, generated during build process
|
||||
*.a
|
||||
|
||||
# Libtool library files generated during build process
|
||||
.#*
|
||||
*.
|
||||
*.slo
|
||||
*.mk
|
||||
*.mem
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.la
|
||||
|
||||
# Directories created by Libtool for storing generated library files
|
||||
.libs/
|
||||
|
||||
# Library object files generated during build process
|
||||
*.lo
|
||||
|
||||
# Temporary library object files generated by compiler and libtool due to failure
|
||||
*.loT
|
||||
|
||||
# Standard object files generated during build process
|
||||
*.o
|
||||
|
||||
# Dependency files generated during build process
|
||||
*.dep
|
||||
|
||||
# Cache directories created by Autoconf tools
|
||||
autom4te.cache/
|
||||
|
||||
# Cache file(s) generated by Autoconf's configure when run as `configure -C`
|
||||
config.cache
|
||||
|
||||
# A log file(s) generated by Autoconf during *nix build system
|
||||
config.log
|
||||
|
||||
# Helper(s) generated by *nix build system to save the last configure command
|
||||
config.nice
|
||||
|
||||
# Generated by Autoconf's configure script for instantiating templates
|
||||
config.status
|
||||
|
||||
# Main *nix build system configuration script(s) generated by Autoconf
|
||||
configure
|
||||
|
||||
# Generated by `./configure` when test programs create temporary files
|
||||
confdefs.h
|
||||
conftest*
|
||||
|
||||
# Generated by configure scripts on all systems
|
||||
/main/internal_functions.c
|
||||
/main/internal_functions_cli.c
|
||||
|
||||
# The Libtool script(s) generated by the *nix build system
|
||||
libtool
|
||||
|
||||
# Makefile(s) generated from Makefile fragment templates by `./configure`
|
||||
*.a
|
||||
*.ncb
|
||||
*.opt
|
||||
*.plg
|
||||
*swp
|
||||
*.patch
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
*.tar.bz2
|
||||
.FBCIndex
|
||||
.FBCLockFolder
|
||||
.deps
|
||||
.libs
|
||||
phpt.*
|
||||
core
|
||||
dynlib.m4
|
||||
Debug
|
||||
Debug_TS
|
||||
Makefile
|
||||
Makefile.fragments
|
||||
Makefile.objects
|
||||
|
||||
# Directories for shared object files and headers generated by `./configure`
|
||||
include/
|
||||
libs/
|
||||
modules/
|
||||
|
||||
# Used by build/gen_stub.php
|
||||
build/PHP-Parser-*
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Configuration headers generated by the PHP build system
|
||||
# ------------------------------------------------------------------------------
|
||||
Release
|
||||
Release_TS
|
||||
Release_TSDbg
|
||||
Release_TS_inline
|
||||
Release_inline
|
||||
ZendEngine1
|
||||
_libs
|
||||
acconfig.h
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
bsd_converted
|
||||
buildconf.stamp
|
||||
buildmk.stamp
|
||||
confdefs.h
|
||||
config.h
|
||||
config.guess
|
||||
config.cache
|
||||
config.h.in
|
||||
/ext/date/lib/timelib_config.h
|
||||
/main/build-defs.h
|
||||
/main/php_config.h.in
|
||||
/main/php_config.h
|
||||
/Zend/zend_config.h
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Manual (man 1 and 8) pages generated from templates for *nix alike systems
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/phar/phar.1
|
||||
/ext/phar/phar.phar.1
|
||||
/sapi/cgi/php-cgi.1
|
||||
/sapi/cli/php.1
|
||||
/sapi/fpm/php-fpm.1
|
||||
/sapi/fpm/php-fpm.8
|
||||
/sapi/phpdbg/phpdbg.1
|
||||
/scripts/man1/*.1
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# SAPIs specific ignores
|
||||
# ------------------------------------------------------------------------------
|
||||
/sapi/apache2handler/libphp.module
|
||||
/sapi/fpm/fpm/php-cgi
|
||||
/sapi/fpm/init.d.php-fpm
|
||||
/sapi/fpm/php-fpm.conf
|
||||
/sapi/fpm/php-fpm.service
|
||||
/sapi/fpm/status.html
|
||||
/sapi/fpm/www.conf
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Executable binaries and scripts generated during the build process
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/phar/phar.phar
|
||||
/ext/phar/phar.php
|
||||
/pear/install-pear-nozlib.phar
|
||||
/sapi/cgi/php-cgi
|
||||
/sapi/fpm/php-fpm
|
||||
/sapi/phpdbg/phpdbg
|
||||
/scripts/php-config
|
||||
/scripts/phpize
|
||||
config.log
|
||||
config.nice
|
||||
config.nice.bat
|
||||
config.status
|
||||
config.sub
|
||||
config_vars.mk
|
||||
configuration-parser.c
|
||||
configuration-parser.h
|
||||
configuration-parser.output
|
||||
configuration-scanner.c
|
||||
configure
|
||||
configure.bat
|
||||
configure.js
|
||||
conftest
|
||||
conftest.c
|
||||
debug.log
|
||||
diff
|
||||
generated_lists
|
||||
include
|
||||
install-sh
|
||||
internal_functions.c
|
||||
lcov_data
|
||||
lcov_html
|
||||
libs
|
||||
libtool
|
||||
meta_cc
|
||||
meta_ccld
|
||||
missing
|
||||
mkinstalldirs
|
||||
modules
|
||||
php
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Lexer files generated by re2c
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/json/json_scanner.c
|
||||
/ext/json/php_json_scanner_defs.h
|
||||
/ext/pdo/pdo_sql_parser.c
|
||||
/ext/phar/phar_path_check.c
|
||||
/ext/standard/url_scanner_ex.c
|
||||
/ext/standard/var_unserializer.c
|
||||
/sapi/phpdbg/phpdbg_lexer.c
|
||||
/Zend/zend_ini_scanner.c
|
||||
/Zend/zend_ini_scanner_defs.h
|
||||
/Zend/zend_language_scanner.c
|
||||
/Zend/zend_language_scanner_defs.h
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# PHP parser files generated by bison during the build process
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/json/json_parser.tab.h
|
||||
/ext/json/json_parser.tab.c
|
||||
/sapi/phpdbg/phpdbg_parser.c
|
||||
/sapi/phpdbg/phpdbg_parser.h
|
||||
/sapi/phpdbg/phpdbg_parser.output
|
||||
/Zend/zend_ini_parser.c
|
||||
/Zend/zend_ini_parser.h
|
||||
/Zend/zend_ini_parser.output
|
||||
/Zend/zend_language_parser.c
|
||||
/Zend/zend_language_parser.h
|
||||
/Zend/zend_language_parser.output
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Extensions files
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Miscellaneous extensions files
|
||||
/ext/opcache/jit/zend_jit_x86.c
|
||||
/ext/opcache/minilua
|
||||
|
||||
# Generated by `cd ext/name && phpize && ./configure`
|
||||
/ext/*/build/
|
||||
/ext/*/configure.ac
|
||||
/ext/*/run-tests.php
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Generated by Windows build system
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Generated by `/buildconf.bat`
|
||||
/configure.bat
|
||||
/configure.js
|
||||
|
||||
# Generated by `/configure.(bat|js)` (architecture dependent)
|
||||
/config.nice.bat
|
||||
|
||||
# NTS debug build x86
|
||||
/Debug/
|
||||
|
||||
# ZTS debug build x86
|
||||
/Debug_TS/
|
||||
|
||||
# NTS build x86
|
||||
/Release/
|
||||
|
||||
# ZTS build x86
|
||||
/Release_TS/
|
||||
|
||||
# Generated by x64 compiler, includes Debug, Debug_TS, Release, Release_TS
|
||||
/x64/
|
||||
|
||||
# Miscellaneous files generated by Windows build system
|
||||
/main/config.w32.h
|
||||
/win32/build/deplister.exe
|
||||
/win32/build/deplister.obj
|
||||
/win32/*.aps
|
||||
/win32/*.positions
|
||||
/win32/*.suo
|
||||
/win32/ext
|
||||
/win32/syslog.reg
|
||||
/win32/wsyslog.h
|
||||
|
||||
# Standard object files generated by Visual Studio
|
||||
*.obj
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Tests
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Generated by `./run-tests.php` upon failure
|
||||
**/tests/**/*.diff
|
||||
**/tests/**/*.out*
|
||||
**/tests/**/*.php
|
||||
**/tests/**/*.exp
|
||||
**/tests/**/*.log
|
||||
**/tests/**/*.sh
|
||||
|
||||
# Generated by some test cases
|
||||
**/tests/**/*.db
|
||||
|
||||
# Microsoft Access database created for passing to tests
|
||||
/ext/pdo_odbc/tests/*.mdb
|
||||
|
||||
# Files generated by `./run-tests.php -m` when checking for memory leaks
|
||||
**/tests/**/*.mem
|
||||
|
||||
# Test results generated by `./run-tests.php`
|
||||
php-*.tar.gz
|
||||
php5.spec
|
||||
php_lcov.info
|
||||
php_test_results_*.txt
|
||||
|
||||
# Temporary test information generated by `./run-tests.php`
|
||||
/run-test-info.php
|
||||
|
||||
# Temporary POST data placeholder files generated by `./run-tests.php`
|
||||
phpt.*
|
||||
|
||||
# Temporary PHP INI configuration file(s) for tests, generated by `make test`
|
||||
php_version.h
|
||||
results.txt
|
||||
shlibtool
|
||||
stamp-h
|
||||
test.php3
|
||||
tmp-php.ini
|
||||
want_dependencies
|
||||
x64
|
||||
stamp-h.in
|
||||
scan_makefile_in.awk
|
||||
TSRM/tsrm_config.h
|
||||
Zend/zend_config.h
|
||||
Zend/zend_dtrace_gen.h
|
||||
Zend/zend_dtrace_gen.hbak
|
||||
Zend/zend_ini_parser.c
|
||||
Zend/zend_ini_parser.h
|
||||
Zend/zend_ini_parser.output
|
||||
Zend/zend_language_parser.c
|
||||
Zend/zend_language_parser.h
|
||||
Zend/zend_language_parser.output
|
||||
ext/date/lib/timelib_config.h
|
||||
ext/iconv/php_iconv_aliased_libiconv.h
|
||||
ext/phar/phar.php
|
||||
main/php_config.h
|
||||
main/php_config.h.in
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Generated by GCC's gcov and LCOV via build/Makefile.gcov and gcov.php.net
|
||||
# ------------------------------------------------------------------------------
|
||||
*.gcda
|
||||
*.gcno
|
||||
/gcovr.xml
|
||||
/gcovr_html/
|
||||
/lcov_html/
|
||||
/php_lcov.info
|
||||
# Test specific Ignores
|
||||
tests/*/*.diff
|
||||
tests/*/*.out
|
||||
tests/*/*.php
|
||||
tests/*/*.exp
|
||||
tests/*/*.log
|
||||
tests/*/*.sh
|
||||
*/tests/*.diff
|
||||
*/tests/*.out
|
||||
*/tests/*.php
|
||||
*/tests/*.exp
|
||||
*/tests/*.log
|
||||
*/tests/*.sh
|
||||
*/tests/*/*.diff
|
||||
*/tests/*/*.out
|
||||
*/tests/*/*.php
|
||||
*/tests/*/*.exp
|
||||
*/tests/*/*.log
|
||||
*/tests/*/*.sh
|
||||
*/*/tests/*.diff
|
||||
*/*/tests/*.out
|
||||
*/*/tests/*.php
|
||||
*/*/tests/*.exp
|
||||
*/*/tests/*.log
|
||||
*/*/tests/*.sh
|
||||
*/*/tests/*/*.diff
|
||||
*/*/tests/*/*.out
|
||||
*/*/tests/*/*.php
|
||||
*/*/tests/*/*.exp
|
||||
*/*/tests/*/*.log
|
||||
*/*/tests/*/*.sh
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Archives generated during the PHP release process
|
||||
# ------------------------------------------------------------------------------
|
||||
/*.tar.gz
|
||||
/*.tar.bz2
|
||||
/*.tar.xz
|
||||
/*.tar.gz.asc
|
||||
/*.tar.bz2.asc
|
||||
/*.tar.xz.asc
|
||||
# Extension specifc ignores
|
||||
ext/*/configure.in
|
||||
ext/*/ltmain.sh
|
||||
ext/*/libs.mk
|
||||
ext/*/build
|
||||
ext/*/scan_makefile_in.awk
|
||||
ext/*/Makefile.global
|
||||
ext/*/acinclude.m4
|
||||
ext/*/config.sub
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Generated by `./configure --enable-dtrace` for using DTrace framework
|
||||
# ------------------------------------------------------------------------------
|
||||
/ext/oci8/oci8_dtrace_gen.h
|
||||
/ext/oci8/oci8_dtrace_gen.h.bak
|
||||
/Zend/zend_dtrace_gen.h
|
||||
/Zend/zend_dtrace_gen.h.bak
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Special cases to invert previous ignore patterns
|
||||
# ------------------------------------------------------------------------------
|
||||
!/ext/bcmath/libbcmath/src/config.h
|
||||
!/ext/fileinfo/libmagic/config.h
|
||||
!/ext/fileinfo/libmagic.patch
|
||||
!/ext/fileinfo/magicdata.patch
|
||||
!/ext/pcre/pcre2lib/config.h
|
||||
!/win32/build/Makefile
|
||||
# Extension specific
|
||||
ext/iconv/scan_makefile_in.awk
|
||||
ext/iconv/php_have_bsd_iconv.h
|
||||
ext/iconv/php_have_ibm_iconv.h
|
||||
ext/iconv/php_have_libiconv.h
|
||||
ext/iconv/php_php_iconv_h_path.h
|
||||
ext/iconv/php_have_glibc_iconv.h
|
||||
ext/iconv/php_php_iconv_impl.h
|
||||
ext/iconv/php_have_iconv.h
|
||||
ext/iconv/php_iconv_supports_errno.h
|
||||
ext/mbstring/libmbfl/Makefile.in
|
||||
ext/mbstring/libmbfl/autoscan.log
|
||||
ext/mbstring/libmbfl/config.sub
|
||||
ext/mbstring/libmbfl/depcomp
|
||||
ext/mbstring/libmbfl/ltmain.sh
|
||||
ext/mbstring/libmbfl/stamp-h1
|
||||
ext/mbstring/libmbfl/*.dll
|
||||
ext/mbstring/libmbfl/*.lib
|
||||
ext/mbstring/libmbfl/*.RES
|
||||
ext/mbstring/libmbfl/*.res
|
||||
ext/mbstring/libmbfl/*.obj
|
||||
ext/mbstring/libmbfl/*.tds
|
||||
ext/mbstring/libmbfl/filters/Makefile.in
|
||||
ext/mbstring/libmbfl/mbfl/Makefile.in
|
||||
ext/mbstring/libmbfl/mbfl/EastAsianWidth.txt
|
||||
ext/mbstring/libmbfl/nls/Makefile.in
|
||||
ext/mssql/#*#
|
||||
ext/mssql/Makefile.global
|
||||
ext/mssql/acinclude.m4
|
||||
ext/mssql/config.sub
|
||||
ext/mssql/configure.in
|
||||
ext/mssql/ltmain.sh
|
||||
ext/mysql/weztest.sqlite
|
||||
ext/oci8/tests/*.tmp
|
||||
ext/oci8/tests/*.vglog
|
||||
ext/pdo/conftest*
|
||||
ext/pdo/tests/*.db
|
||||
ext/pdo/tests/*.tmp
|
||||
ext/pdo_firebird/conftest*
|
||||
ext/pdo_mysql/conftest*
|
||||
ext/pdo_mysql/tests/*.db
|
||||
ext/pdo_mysql/tests/*.tmp
|
||||
ext/pdo_oci/conftest*
|
||||
ext/pdo_odbc/tests/*.db
|
||||
ext/pdo_odbc/tests/*.tmp
|
||||
ext/pdo_odbc/tests/*.mdb
|
||||
ext/pdo_pgsql/conftest*
|
||||
ext/pdo_pgsql/tests/*.db
|
||||
ext/pdo_pgsql/tests/*.tmp
|
||||
ext/pdo_sqlite/conftest*
|
||||
ext/pdo_sqlite/sqlite3.h
|
||||
ext/pdo_sqlite/tests/*.db
|
||||
ext/pdo_sqlite/tests/*.tmp
|
||||
ext/phar/phar.phar
|
||||
ext/pspell/tests/*.tmp
|
||||
ext/reflection/xml
|
||||
ext/reflection/html
|
||||
ext/reflection/spl.chm
|
||||
ext/simplexml/examples/security.new.xml
|
||||
ext/spl/examples/.htaccess
|
||||
ext/spl/examples/*.phps
|
||||
ext/sqlite/weztest.sqlite
|
||||
ext/sqlite/libsqlite/src/sqlite.h
|
||||
ext/sqlite/libsqlite/src/parse.out
|
||||
ext/sqlite/libsqlite/src/libsqlite.dsw
|
||||
ext/sqlite3/tests/phpsql*
|
||||
ext/sqlite3/tests/*.db
|
||||
ext/sqlite3/tests/*.tmp
|
||||
ext/standard/tests/misc/*.txt
|
||||
ext/standard/tests/strings/*.txt
|
||||
main/build-defs.h
|
||||
main/internal_functions_cli.c
|
||||
main/config.w32.h
|
||||
main/stamp-h1
|
||||
main/streams/build-defs.h
|
||||
main/streams/stamp-h1
|
||||
pear/install-pear-nozlib.phar
|
||||
pear/phpize
|
||||
pear/run-tests
|
||||
pear/php-config
|
||||
pear/scripts
|
||||
sapi/apache/libphp5.module
|
||||
sapi/apache2handler/libphp5.module
|
||||
sapi/apache_hooks/libphp5.module
|
||||
sapi/cgi/php-cgi
|
||||
sapi/cli/php.1
|
||||
sapi/fpm/php-fpm
|
||||
sapi/fpm/php-fpm.1
|
||||
sapi/fpm/init.d.php-fpm
|
||||
sapi/fpm/php-fpm.conf
|
||||
sapi/fpm/fpm/php-cgi
|
||||
scripts/php-config
|
||||
scripts/phpize
|
||||
scripts/man1/*.1
|
||||
win32/*.suo
|
||||
win32/*.aps
|
||||
win32/*.positions
|
||||
win32/ext
|
||||
win32/phpts.def
|
||||
win32/wsyslog.h
|
||||
win32/win32.dsp
|
||||
win32/php.dsw
|
||||
+15
-92
@@ -1,101 +1,24 @@
|
||||
git:
|
||||
quiet: true
|
||||
language: php
|
||||
|
||||
dist: bionic
|
||||
language: c
|
||||
os: linux
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- locales
|
||||
- language-pack-de
|
||||
- re2c
|
||||
- ccache
|
||||
- mysql-server
|
||||
- libaspell-dev
|
||||
- libbz2-dev
|
||||
- libcurl4-gnutls-dev
|
||||
- libenchant-dev
|
||||
- libfreetype6-dev
|
||||
- libgmp-dev
|
||||
- libicu-dev
|
||||
- libjpeg-dev
|
||||
- libkrb5-dev
|
||||
- libonig-dev
|
||||
- libpng-dev
|
||||
- libpq-dev
|
||||
- libpspell-dev
|
||||
- libsasl2-dev
|
||||
- libsqlite3-dev
|
||||
- libsodium-dev
|
||||
- libtidy-dev
|
||||
- libwebp-dev
|
||||
- libxml2-dev
|
||||
- libxpm-dev
|
||||
- libxslt1-dev
|
||||
- libzip-dev
|
||||
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- postgresql
|
||||
php:
|
||||
# We only specify one version so we only get one worker
|
||||
- 5.4
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_failure: change
|
||||
irc:
|
||||
template:
|
||||
- "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message} - Change view : %{compare_url} - Build details : %{build_url}"
|
||||
channels:
|
||||
- "irc.efnet.org#php.pecl"
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
||||
cache:
|
||||
apt: true
|
||||
ccache: true
|
||||
email: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- MYSQL_TEST_HOST=127.0.0.1
|
||||
- MYSQL_TEST_USER=travis
|
||||
- PDO_MYSQL_TEST_DSN="mysql:host=127.0.0.1;dbname=test"
|
||||
- PDO_MYSQL_TEST_USER=travis
|
||||
- PDO_MYSQL_TEST_PASS=
|
||||
- PDO_MYSQL_TEST_HOST=127.0.0.1
|
||||
- PDO_PGSQL_TEST_DSN="pgsql:host=localhost port=5432 dbname=test user=postgres password="
|
||||
- REPORT_EXIT_STATUS=1
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- env: ENABLE_ZTS=1 ENABLE_DEBUG=1 SKIP_IO_CAPTURE_TESTS=1 ARM64=1
|
||||
arch: arm64-graviton2
|
||||
dist: bionic
|
||||
virt: lxd
|
||||
group: edge
|
||||
if: type = cron
|
||||
- env: ENABLE_ZTS=1 ENABLE_DEBUG=1 SKIP_IO_CAPTURE_TESTS=1 S390X=1
|
||||
arch: s390x
|
||||
if: type = cron
|
||||
- REPORT_EXIT_STATUS=1 TEST_PHP_EXECUTABLE=./sapi/cli/php
|
||||
|
||||
before_script:
|
||||
- ccache --version
|
||||
- ccache --zero-stats
|
||||
- export USE_CCACHE=1
|
||||
# Enable IPv6
|
||||
- sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'
|
||||
# Compile PHP
|
||||
- travis_wait ./travis/compile.sh
|
||||
# Setup Extensions
|
||||
- ./travis/setup-mysql.sh
|
||||
- ./travis/setup-pgsql.sh
|
||||
- ./travis/compile.sh
|
||||
# Setup Extensions
|
||||
- . ./travis/ext/mysql/setup.sh
|
||||
- . ./travis/ext/mysqli/setup.sh
|
||||
- . ./travis/ext/pdo_mysql/setup.sh
|
||||
- . ./travis/ext/pgsql/setup.sh
|
||||
- . ./travis/ext/pdo_pgsql/setup.sh
|
||||
|
||||
# Run PHPs run-tests.php
|
||||
script:
|
||||
# ARM64 CI reports nproc=32, which is excessive.
|
||||
- if [ -z "$ARM64" ]; then export JOBS=$(nproc); else export JOBS=16; fi
|
||||
- ./sapi/cli/php run-tests.php -P -d extension=`pwd`/modules/zend_test.so $(if [ $ENABLE_DEBUG == 0 ]; then echo "-d opcache.enable_cli=1 -d opcache.protect_memory=1 -d opcache.jit_buffer_size=16M -d zend_extension=`pwd`/modules/opcache.so"; fi) -g "FAIL,XFAIL,BORK,WARN,LEAK,SKIP" --offline --show-diff --show-slow 1000 --set-timeout 120 -j$JOBS
|
||||
- sapi/cli/php -d extension_dir=`pwd`/modules -r 'dl("zend_test");'
|
||||
|
||||
after_success:
|
||||
- ccache --show-stats
|
||||
# Run PHPs run-tests.php
|
||||
script: ./sapi/cli/php run-tests.php -g "FAIL,XFAIL,BORK,WARN,LEAK,SKIP"
|
||||
@@ -0,0 +1,277 @@
|
||||
========================
|
||||
PHP Coding Standards
|
||||
========================
|
||||
|
||||
This file lists several standards that any programmer adding or changing
|
||||
code in PHP should follow. Since this file was added at a very late
|
||||
stage of the development of PHP v3.0, the code base does not (yet) fully
|
||||
follow it, but it's going in that general direction. Since we are now
|
||||
well into version 5 releases, many sections have been recoded to use
|
||||
these rules.
|
||||
|
||||
Code Implementation
|
||||
-------------------
|
||||
|
||||
0. Document your code in source files and the manual. [tm]
|
||||
|
||||
1. Functions that are given pointers to resources should not free them
|
||||
|
||||
For instance, ``function int mail(char *to, char *from)`` should NOT free
|
||||
to and/or from.
|
||||
Exceptions:
|
||||
|
||||
- The function's designated behavior is freeing that resource. E.g. efree()
|
||||
|
||||
- The function is given a boolean argument, that controls whether or not
|
||||
the function may free its arguments (if true - the function must free its
|
||||
arguments, if false - it must not)
|
||||
|
||||
- Low-level parser routines, that are tightly integrated with the token
|
||||
cache and the bison code for minimum memory copying overhead.
|
||||
|
||||
2. Functions that are tightly integrated with other functions within the
|
||||
same module, and rely on each other non-trivial behavior, should be
|
||||
documented as such and declared 'static'. They should be avoided if
|
||||
possible.
|
||||
|
||||
3. Use definitions and macros whenever possible, so that constants have
|
||||
meaningful names and can be easily manipulated. The only exceptions
|
||||
to this rule are 0 and 1, when used as false and true (respectively).
|
||||
Any other use of a numeric constant to specify different behavior
|
||||
or actions should be done through a #define.
|
||||
|
||||
4. When writing functions that deal with strings, be sure to remember
|
||||
that PHP holds the length property of each string, and that it
|
||||
shouldn't be calculated with strlen(). Write your functions in a such
|
||||
a way so that they'll take advantage of the length property, both
|
||||
for efficiency and in order for them to be binary-safe.
|
||||
Functions that change strings and obtain their new lengths while
|
||||
doing so, should return that new length, so it doesn't have to be
|
||||
recalculated with strlen() (e.g. php_addslashes())
|
||||
|
||||
5. NEVER USE strncat(). If you're absolutely sure you know what you're doing,
|
||||
check its man page again, and only then, consider using it, and even then,
|
||||
try avoiding it.
|
||||
|
||||
6. Use ``PHP_*`` macros in the PHP source, and ``ZEND_*`` macros in the Zend
|
||||
part of the source. Although the ``PHP_*`` macro's are mostly aliased to the
|
||||
``ZEND_*`` macros it gives a better understanding on what kind of macro
|
||||
you're calling.
|
||||
|
||||
7. When commenting out code using a #if statement, do NOT use 0 only. Instead
|
||||
use "<svn username here>_0". For example, #if FOO_0, where FOO is your
|
||||
svn user foo. This allows easier tracking of why code was commented out,
|
||||
especially in bundled libraries.
|
||||
|
||||
8. Do not define functions that are not available. For instance, if a
|
||||
library is missing a function, do not define the PHP version of the
|
||||
function, and do not raise a run-time error about the function not
|
||||
existing. End users should use function_exists() to test for the
|
||||
existence of a function
|
||||
|
||||
9. Prefer emalloc(), efree(), estrdup(), etc. to their standard C library
|
||||
counterparts. These functions implement an internal "safety-net"
|
||||
mechanism that ensures the deallocation of any unfreed memory at the
|
||||
end of a request. They also provide useful allocation and overflow
|
||||
information while running in debug mode.
|
||||
|
||||
In almost all cases, memory returned to the engine must be allocated
|
||||
using emalloc().
|
||||
|
||||
The use of malloc() should be limited to cases where a third-party
|
||||
library may need to control or free the memory, or when the memory in
|
||||
question needs to survive between multiple requests.
|
||||
|
||||
Naming Conventions
|
||||
------------------
|
||||
|
||||
1. Function names for user-level functions should be enclosed with in
|
||||
the PHP_FUNCTION() macro. They should be in lowercase, with words
|
||||
underscore delimited, with care taken to minimize the letter count.
|
||||
Abbreviations should not be used when they greatly decrease the
|
||||
readability of the function name itself::
|
||||
|
||||
Good:
|
||||
'mcrypt_enc_self_test'
|
||||
'mysql_list_fields'
|
||||
|
||||
Ok:
|
||||
'mcrypt_module_get_algo_supported_key_sizes'
|
||||
(could be 'mcrypt_mod_get_algo_sup_key_sizes'?)
|
||||
'get_html_translation_table'
|
||||
(could be 'html_get_trans_table'?)
|
||||
|
||||
Bad:
|
||||
'hw_GetObjectByQueryCollObj'
|
||||
'pg_setclientencoding'
|
||||
'jf_n_s_i'
|
||||
|
||||
2. If they are part of a "parent set" of functions, that parent should
|
||||
be included in the user function name, and should be clearly related
|
||||
to the parent program or function family. This should be in the form
|
||||
of ``parent_*``::
|
||||
|
||||
A family of 'foo' functions, for example:
|
||||
Good:
|
||||
'foo_select_bar'
|
||||
'foo_insert_baz'
|
||||
'foo_delete_baz'
|
||||
|
||||
Bad:
|
||||
'fooselect_bar'
|
||||
'fooinsertbaz'
|
||||
'delete_foo_baz'
|
||||
|
||||
3. Function names used by user functions should be prefixed
|
||||
with ``_php_``, and followed by a word or an underscore-delimited list of
|
||||
words, in lowercase letters, that describes the function. If applicable,
|
||||
they should be declared 'static'.
|
||||
|
||||
4. Variable names must be meaningful. One letter variable names must be
|
||||
avoided, except for places where the variable has no real meaning or
|
||||
a trivial meaning (e.g. for (i=0; i<100; i++) ...).
|
||||
|
||||
5. Variable names should be in lowercase. Use underscores to separate
|
||||
between words.
|
||||
|
||||
6. Method names follow the 'studlyCaps' (also referred to as 'bumpy case'
|
||||
or 'camel caps') naming convention, with care taken to minimize the
|
||||
letter count. The initial letter of the name is lowercase, and each
|
||||
letter that starts a new 'word' is capitalized::
|
||||
|
||||
Good:
|
||||
'connect()'
|
||||
'getData()'
|
||||
'buildSomeWidget()'
|
||||
|
||||
Bad:
|
||||
'get_Data()'
|
||||
'buildsomewidget'
|
||||
'getI()'
|
||||
|
||||
7. Classes should be given descriptive names. Avoid using abbreviations where
|
||||
possible. Each word in the class name should start with a capital letter,
|
||||
without underscore delimiters (CampelCaps starting with a capital letter).
|
||||
The class name should be prefixed with the name of the 'parent set' (e.g.
|
||||
the name of the extension)::
|
||||
|
||||
Good:
|
||||
'Curl'
|
||||
'FooBar'
|
||||
|
||||
Bad:
|
||||
'foobar'
|
||||
'foo_bar'
|
||||
|
||||
Syntax and indentation
|
||||
----------------------
|
||||
|
||||
1. Never use C++ style comments (i.e. // comment). Always use C-style
|
||||
comments instead. PHP is written in C, and is aimed at compiling
|
||||
under any ANSI-C compliant compiler. Even though many compilers
|
||||
accept C++-style comments in C code, you have to ensure that your
|
||||
code would compile with other compilers as well.
|
||||
The only exception to this rule is code that is Win32-specific,
|
||||
because the Win32 port is MS-Visual C++ specific, and this compiler
|
||||
is known to accept C++-style comments in C code.
|
||||
|
||||
2. Use K&R-style. Of course, we can't and don't want to
|
||||
force anybody to use a style he or she is not used to, but,
|
||||
at the very least, when you write code that goes into the core
|
||||
of PHP or one of its standard modules, please maintain the K&R
|
||||
style. This applies to just about everything, starting with
|
||||
indentation and comment styles and up to function declaration
|
||||
syntax. Also see Indentstyle_.
|
||||
|
||||
.. _Indentstyle: http://www.catb.org/~esr/jargon/html/I/indent-style.html
|
||||
|
||||
3. Be generous with whitespace and braces. Keep one empty line between the
|
||||
variable declaration section and the statements in a block, as well as
|
||||
between logical statement groups in a block. Maintain at least one empty
|
||||
line between two functions, preferably two. Always prefer::
|
||||
|
||||
if (foo) {
|
||||
bar;
|
||||
}
|
||||
|
||||
to:
|
||||
|
||||
if(foo)bar;
|
||||
|
||||
4. When indenting, use the tab character. A tab is expected to represent
|
||||
four spaces. It is important to maintain consistency in indenture so
|
||||
that definitions, comments, and control structures line up correctly.
|
||||
|
||||
5. Preprocessor statements (#if and such) MUST start at column one. To
|
||||
indent preprocessor directives you should put the # at the beginning
|
||||
of a line, followed by any number of whitespace.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
||||
1. Extensions should be well tested using *.phpt tests. Read about that
|
||||
in README.TESTING.
|
||||
|
||||
Documentation and Folding Hooks
|
||||
-------------------------------
|
||||
|
||||
In order to make sure that the online documentation stays in line with
|
||||
the code, each user-level function should have its user-level function
|
||||
prototype before it along with a brief one-line description of what the
|
||||
function does. It would look like this::
|
||||
|
||||
/* {{{ proto int abs(int number)
|
||||
Returns the absolute value of the number */
|
||||
PHP_FUNCTION(abs)
|
||||
{
|
||||
...
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
The {{{ symbols are the default folding symbols for the folding mode in
|
||||
Emacs and vim (set fdm=marker). Folding is very useful when dealing with
|
||||
large files because you can scroll through the file quickly and just unfold
|
||||
the function you wish to work on. The }}} at the end of each function marks
|
||||
the end of the fold, and should be on a separate line.
|
||||
|
||||
The "proto" keyword there is just a helper for the doc/genfuncsummary script
|
||||
which generates a full function summary. Having this keyword in front of the
|
||||
function prototypes allows us to put folds elsewhere in the code without
|
||||
messing up the function summary.
|
||||
|
||||
Optional arguments are written like this::
|
||||
|
||||
/* {{{ proto object imap_header(int stream_id, int msg_no [, int from_length [, int subject_length [, string default_host]]])
|
||||
Returns a header object with the defined parameters */
|
||||
|
||||
And yes, please keep the prototype on a single line, even if that line
|
||||
is massive.
|
||||
|
||||
New and Experimental Functions
|
||||
-----------------------------------
|
||||
To reduce the problems normally associated with the first public
|
||||
implementation of a new set of functions, it has been suggested
|
||||
that the first implementation include a file labeled 'EXPERIMENTAL'
|
||||
in the function directory, and that the functions follow the
|
||||
standard prefixing conventions during their initial implementation.
|
||||
|
||||
The file labelled 'EXPERIMENTAL' should include the following
|
||||
information::
|
||||
|
||||
Any authoring information (known bugs, future directions of the module).
|
||||
Ongoing status notes which may not be appropriate for SVN comments.
|
||||
|
||||
Aliases & Legacy Documentation
|
||||
-----------------------------------
|
||||
You may also have some deprecated aliases with close to duplicate
|
||||
names, for example, somedb_select_result and somedb_selectresult. For
|
||||
documentation purposes, these will only be documented by the most
|
||||
current name, with the aliases listed in the documentation for
|
||||
the parent function. For ease of reference, user-functions with
|
||||
completely different names, that alias to the same function (such as
|
||||
highlight_file and show_source), will be separately documented. The
|
||||
proto should still be included, describing which function is aliased.
|
||||
|
||||
Backwards compatible functions and names should be maintained as long
|
||||
as the code can be reasonably be kept as part of the codebase. See
|
||||
/phpdoc/README for more information on documentation.
|
||||
@@ -1,320 +0,0 @@
|
||||
# PHP coding standards
|
||||
|
||||
This file lists several standards that any programmer adding or changing code in
|
||||
PHP should follow. Since this file was added at a very late stage of the
|
||||
development of PHP v3.0, the code base does not fully follow it, but new
|
||||
features are going in that general direction. Many sections have been rewritten to
|
||||
use these rules.
|
||||
|
||||
## Code implementation
|
||||
|
||||
1. Document your code in source files and the manual. (tm)
|
||||
|
||||
2. Functions that are given pointers to resources should not free them.
|
||||
|
||||
For instance, `function int mail(char *to, char *from)` should NOT free to
|
||||
and/or from.
|
||||
|
||||
Exceptions:
|
||||
|
||||
* The function's designated behavior is freeing that resource. E.g.
|
||||
`efree()`
|
||||
|
||||
* The function is given a boolean argument, that controls whether or not the
|
||||
function may free its arguments (if true - the function must free its
|
||||
arguments, if false - it must not)
|
||||
|
||||
* Low-level parser routines, that are tightly integrated with the token
|
||||
cache and the bison code for minimum memory copying overhead.
|
||||
|
||||
3. Functions that are tightly integrated with other functions within the same
|
||||
module, and rely on each other non-trivial behavior, should be documented as
|
||||
such and declared `static`. They should be avoided if possible.
|
||||
|
||||
4. Use definitions and macros whenever possible, so that constants have
|
||||
meaningful names and can be easily manipulated. The only exceptions to this
|
||||
rule are 0 and 1, when used as `false` and `true` (respectively). Any other
|
||||
use of a numeric constant to specify different behavior or actions should be
|
||||
done through a `#define`.
|
||||
|
||||
5. When writing functions that deal with strings, be sure to remember that PHP
|
||||
holds the length property of each string, and that it shouldn't be
|
||||
calculated with `strlen()`. Write your functions in such a way so that
|
||||
they'll take advantage of the length property, both for efficiency and in
|
||||
order for them to be binary-safe. Functions that change strings and obtain
|
||||
their new lengths while doing so, should return that new length, so it
|
||||
doesn't have to be recalculated with `strlen()` (e.g. `php_addslashes()`).
|
||||
|
||||
6. NEVER USE `strncat()`. If you're absolutely sure you know what you're doing,
|
||||
check its man page again, and only then, consider using it, and even then,
|
||||
try avoiding it.
|
||||
|
||||
7. Use `PHP_*` macros in the PHP source, and `ZEND_*` macros in the Zend part of
|
||||
the source. Although the `PHP_*` macros are mostly aliased to the `ZEND_*`
|
||||
macros it gives a better understanding on what kind of macro you're calling.
|
||||
|
||||
8. When commenting out code using a `#if` statement, do NOT use `0` only.
|
||||
Instead use `"<git username here>_0"`. For example, `#if FOO_0`, where `FOO`
|
||||
is your git user `foo`. This allows easier tracking of why code was
|
||||
commented out, especially in bundled libraries.
|
||||
|
||||
9. Do not define functions that are not available. For instance, if a library is
|
||||
missing a function, do not define the PHP version of the function, and do
|
||||
not raise a run-time error about the function not existing. End users should
|
||||
use `function_exists()` to test for the existence of a function.
|
||||
|
||||
10. Prefer `emalloc()`, `efree()`, `estrdup()`, etc. to their standard C library
|
||||
counterparts. These functions implement an internal "safety-net" mechanism
|
||||
that ensures the deallocation of any unfreed memory at the end of a request.
|
||||
They also provide useful allocation and overflow information while running
|
||||
in debug mode.
|
||||
|
||||
In almost all cases, memory returned to the engine must be allocated using
|
||||
`emalloc()`.
|
||||
|
||||
The use of `malloc()` should be limited to cases where a third-party library
|
||||
may need to control or free the memory, or when the memory in question needs
|
||||
to survive between multiple requests.
|
||||
|
||||
## User functions/methods naming conventions
|
||||
|
||||
1. Function names for user-level functions should be enclosed with in the
|
||||
`PHP_FUNCTION()` macro. They should be in lowercase, with words underscore
|
||||
delimited, with care taken to minimize the letter count. Abbreviations
|
||||
should not be used when they greatly decrease the readability of the
|
||||
function name itself:
|
||||
|
||||
Good:
|
||||
|
||||
```php
|
||||
str_word_count
|
||||
array_key_exists
|
||||
```
|
||||
|
||||
Ok:
|
||||
|
||||
```php
|
||||
date_interval_create_from_date_string
|
||||
// Could be 'date_intvl_create_from_date_str'?
|
||||
get_html_translation_table()
|
||||
// Could be 'html_get_trans_table'?
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```php
|
||||
hw_GetObjectByQueryCollObj
|
||||
pg_setclientencoding
|
||||
jf_n_s_i
|
||||
```
|
||||
|
||||
2. If they are part of a "parent set" of functions, that parent should be
|
||||
included in the user function name, and should be clearly related to the
|
||||
parent program or function family. This should be in the form of `parent_*`:
|
||||
|
||||
A family of `foo` functions, for example:
|
||||
|
||||
Good:
|
||||
|
||||
```php
|
||||
foo_select_bar
|
||||
foo_insert_baz
|
||||
foo_delete_baz
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```php
|
||||
fooselect_bar
|
||||
fooinsertbaz
|
||||
delete_foo_baz
|
||||
```
|
||||
|
||||
3. Function names used by user functions should be prefixed with `_php_`, and
|
||||
followed by a word or an underscore-delimited list of words, in lowercase
|
||||
letters, that describes the function. If applicable, they should be declared
|
||||
`static`.
|
||||
|
||||
4. Variable names must be meaningful. One letter variable names must be avoided,
|
||||
except for places where the variable has no real meaning or a trivial
|
||||
meaning (e.g. `for (i=0; i<100; i++) ...`).
|
||||
|
||||
5. Variable names should be in lowercase. Use underscores to separate between
|
||||
words.
|
||||
|
||||
6. Method names follow the *studlyCaps* (also referred to as *bumpy case* or
|
||||
*camel caps*) naming convention, with care taken to minimize the letter
|
||||
count. The initial letter of the name is lowercase, and each letter that
|
||||
starts a new `word` is capitalized:
|
||||
|
||||
Good:
|
||||
|
||||
```php
|
||||
connect()
|
||||
getData()
|
||||
buildSomeWidget()
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```php
|
||||
get_Data()
|
||||
buildsomewidget()
|
||||
getI()
|
||||
```
|
||||
|
||||
7. Class names should be descriptive nouns in *PascalCase* and as short as
|
||||
possible. Each word in the class name should start with a capital letter,
|
||||
without underscore delimiters. The class name should be prefixed with the
|
||||
name of the "parent set" (e.g. the name of the extension) if no namespaces
|
||||
are used. Abbreviations and acronyms as well as initialisms should be
|
||||
avoided wherever possible, unless they are much more widely used than the
|
||||
long form (e.g. HTTP or URL). Abbreviations start with a capital letter
|
||||
followed by lowercase letters, whereas acronyms and initialisms are written
|
||||
according to their standard notation. Usage of acronyms and initialisms is
|
||||
not allowed if they are not widely adopted and recognized as such.
|
||||
|
||||
Good:
|
||||
|
||||
```php
|
||||
Curl
|
||||
CurlResponse
|
||||
HTTPStatusCode
|
||||
URL
|
||||
BTreeMap // B-tree Map
|
||||
Id // Identifier
|
||||
ID // Identity Document
|
||||
Char // Character
|
||||
Intl // Internationalization
|
||||
Radar // Radio Detecting and Ranging
|
||||
```
|
||||
|
||||
Bad:
|
||||
|
||||
```php
|
||||
curl
|
||||
curl_response
|
||||
HttpStatusCode
|
||||
Url
|
||||
BtreeMap
|
||||
ID // Identifier
|
||||
CHAR
|
||||
INTL
|
||||
RADAR // Radio Detecting and Ranging
|
||||
```
|
||||
|
||||
## Internal function naming conventions
|
||||
|
||||
1. Functions that are part of the external API should be named
|
||||
`php_modulename_function()` to avoid symbol collision. They should be in
|
||||
lowercase, with words underscore delimited. Exposed API must be defined in
|
||||
`php_modulename.h`.
|
||||
|
||||
```c
|
||||
PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS);
|
||||
```
|
||||
|
||||
Unexposed module function should be static and should not be defined in
|
||||
`php_modulename.h`.
|
||||
|
||||
```c
|
||||
static int php_session_destroy()
|
||||
```
|
||||
|
||||
2. Main module source file must be named `modulename.c`.
|
||||
|
||||
3. Header file that is used by other sources must be named `php_modulename.h`.
|
||||
|
||||
## Syntax and indentation
|
||||
|
||||
1. Never use C++ style comments (i.e. `//` comment). Always use C-style comments
|
||||
instead. PHP is written in C, and is aimed at compiling under any ANSI-C
|
||||
compliant compiler. Even though many compilers accept C++-style comments in
|
||||
C code, you have to ensure that your code would compile with other compilers
|
||||
as well. The only exception to this rule is code that is Win32-specific,
|
||||
because the Win32 port is MS-Visual C++ specific, and this compiler is known
|
||||
to accept C++-style comments in C code.
|
||||
|
||||
2. Use K&R-style. Of course, we can't and don't want to force anybody to use a
|
||||
style he or she is not used to, but, at the very least, when you write code
|
||||
that goes into the core of PHP or one of its standard modules, please
|
||||
maintain the K&R style. This applies to just about everything, starting with
|
||||
indentation and comment styles and up to function declaration syntax. Also
|
||||
see [Indentstyle](http://www.catb.org/~esr/jargon/html/I/indent-style.html).
|
||||
|
||||
3. Be generous with whitespace and braces. Keep one empty line between the
|
||||
variable declaration section and the statements in a block, as well as
|
||||
between logical statement groups in a block. Maintain at least one empty
|
||||
line between two functions, preferably two. Always prefer:
|
||||
|
||||
```c
|
||||
if (foo) {
|
||||
bar;
|
||||
}
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```c
|
||||
if(foo)bar;
|
||||
```
|
||||
|
||||
4. When indenting, use the tab character. A tab is expected to represent four
|
||||
spaces. It is important to maintain consistency in indenture so that
|
||||
definitions, comments, and control structures line up correctly.
|
||||
|
||||
5. Preprocessor statements (`#if` and such) MUST start at column one. To indent
|
||||
preprocessor directives you should put the `#` at the beginning of a line,
|
||||
followed by any number of whitespace.
|
||||
|
||||
## Testing
|
||||
|
||||
1. Extensions should be well tested using `*.phpt` tests. Read about that at
|
||||
[qa.php.net](https://qa.php.net/write-test.php) documentation.
|
||||
|
||||
## Folding hooks
|
||||
|
||||
Use `{{{` symbols for the folding mode in Emacs and vim (`set fdm=marker`).
|
||||
Folding is very useful when dealing with large files because you can scroll
|
||||
through the file quickly and just unfold the function you wish to work on.
|
||||
The `}}}` at the end of each function marks the end of the fold, and should
|
||||
be on a separate line.
|
||||
|
||||
```c
|
||||
/* {{{ Returns the absolute value of the number */
|
||||
PHP_FUNCTION(abs)
|
||||
{
|
||||
...
|
||||
}
|
||||
/* }}} */
|
||||
```
|
||||
|
||||
## New and experimental functions
|
||||
|
||||
To reduce the problems normally associated with the first public implementation
|
||||
of a new set of functions, it has been suggested that the first implementation
|
||||
include a file labeled `EXPERIMENTAL` in the function directory, and that the
|
||||
functions follow the standard prefixing conventions during their initial
|
||||
implementation.
|
||||
|
||||
The file labelled `EXPERIMENTAL` should include the following information:
|
||||
|
||||
* Any authoring information (known bugs, future directions of the module).
|
||||
* Ongoing status notes which may not be appropriate for Git comments.
|
||||
|
||||
In general new features should go to PECL or experimental branches until there
|
||||
are specific reasons for directly adding it to the core distribution.
|
||||
|
||||
## Aliases & legacy documentation
|
||||
|
||||
You may also have some deprecated aliases with close to duplicate names, for
|
||||
example, `somedb_select_result` and `somedb_selectresult`. For documentation
|
||||
purposes, these will only be documented by the most current name, with the
|
||||
aliases listed in the documentation for the parent function. For ease of
|
||||
reference, user-functions with completely different names, that alias to the
|
||||
same function (such as `highlight_file` and `show_source`), will be separately
|
||||
documented.
|
||||
|
||||
Backwards compatible functions and names should be maintained as long as the
|
||||
code can be reasonably be kept as part of the codebase. See the `README` in the
|
||||
PHP documentation repository for more information on documentation.
|
||||
-425
@@ -1,425 +0,0 @@
|
||||
# Contributing to PHP
|
||||
|
||||
Anybody who programs in PHP can be a contributing member of the community that
|
||||
develops and deploys it; the task of deploying PHP, documentation and associated
|
||||
websites is a never ending one. With every release, or release candidate comes a
|
||||
wave of work, which takes a lot of organization and co-ordination.
|
||||
|
||||
You don't need any special access to download, build, debug and begin submitting
|
||||
PHP or PECL code, tests or documentation. Once you've followed this guide and
|
||||
had several contributions accepted, commit privileges are often quickly granted.
|
||||
|
||||
## Index
|
||||
|
||||
* [Pull requests](#pull-requests)
|
||||
* [Filing bugs](#filing-bugs)
|
||||
* [Feature requests](#feature-requests)
|
||||
* [Writing tests](#writing-tests)
|
||||
* [Writing documentation](#writing-documentation)
|
||||
* [Getting help](#getting-help)
|
||||
* [PHP source code directory structure](#php-source-code-directory-structure)
|
||||
* [PHP internals](#php-internals)
|
||||
* [PECL extensions](#pecl-extensions)
|
||||
* [Checklist for submitting contribution](#checklist-for-submitting-contribution)
|
||||
* [What happens after submitting contribution?](#what-happens-after-submitting-contribution)
|
||||
* [What happens when your contribution is applied?](#what-happens-when-your-contribution-is-applied)
|
||||
* [Git commit rules](#git-commit-rules)
|
||||
* [Copyright and license headers](#copyright-and-license-headers)
|
||||
|
||||
## Pull requests
|
||||
|
||||
PHP welcomes pull requests to [add tests](#writing-tests), fix bugs and to
|
||||
implement RFCs. Please be sure to include tests as appropriate!
|
||||
|
||||
If you are fixing a bug, then please submit your PR against the lowest actively
|
||||
supported branch of PHP that the bug affects (only green branches on
|
||||
[the supported version page](https://www.php.net/supported-versions.php) are
|
||||
supported). For example, at the time of writing, the lowest supported version is
|
||||
PHP 7.3, which corresponds to the `PHP-7.3` branch in Git. Please also make sure
|
||||
you add a link to the PR in the bug on [the bug tracker](https://bugs.php.net/).
|
||||
|
||||
Pull requests implementing RFCs should be submitted against `master`.
|
||||
|
||||
Pull requests should *never* be submitted against `PHP-x.y.z` branches, as these
|
||||
are only used for release management.
|
||||
|
||||
If your pull request exhibits conflicts with the base branch, please resolve
|
||||
them by using `git rebase` instead of `git merge`.
|
||||
|
||||
Fork the official PHP repository and send a pull request. A notification will be
|
||||
sent to the pull request mailing list. Sending a note to PHP Internals list
|
||||
(internals@lists.php.net) may help getting more feedback and quicker turnaround.
|
||||
You can also add pull requests to [bug reports](https://bugs.php.net/).
|
||||
|
||||
Read [Git access page](https://www.php.net/git.php) for help on using Git to get
|
||||
and build PHP source code. We recommend to look at our
|
||||
[workflow](https://wiki.php.net/vcs/gitworkflow) and our
|
||||
[FAQ](https://wiki.php.net/vcs/gitfaq).
|
||||
|
||||
## Filing bugs
|
||||
|
||||
Bugs can be filed on the [PHP bug tracker](https://bugs.php.net/). If this is
|
||||
the first time you've filed a bug, we suggest reading the
|
||||
[guide to reporting a bug](https://bugs.php.net/how-to-report.php).
|
||||
|
||||
Where possible, please include a self-contained reproduction case!
|
||||
|
||||
## Feature requests
|
||||
|
||||
Feature requests are generally submitted in the form of
|
||||
[Requests for Comments (RFC)](https://wiki.php.net/rfc/howto), ideally
|
||||
accompanied by [pull requests](#pull-requests). You can find the extremely large
|
||||
list of RFCs that have been previously considered on the
|
||||
[PHP Wiki](https://wiki.php.net/rfc).
|
||||
|
||||
To create a RFC, discuss it with the extension maintainer, and discuss it on the
|
||||
development mailing list internals@lists.php.net. RFC Wiki accounts can be
|
||||
requested on https://wiki.php.net/start?do=register. PHP extension maintainers
|
||||
can be found in the [EXTENSIONS](/EXTENSIONS) file in the PHP source code
|
||||
repository. Mailing list subscription is explained on the
|
||||
[mailing lists page](https://www.php.net/mailing-lists.php).
|
||||
|
||||
You may also want to read
|
||||
[The Mysterious PHP RFC Process](https://blogs.oracle.com/opal/entry/the_mysterious_php_rfc_process)
|
||||
for additional notes on the best way to approach submitting an RFC.
|
||||
|
||||
## Writing tests
|
||||
|
||||
We love getting new tests! PHP is a huge project and improving code coverage is
|
||||
a huge win for every PHP user.
|
||||
|
||||
[Our QA site includes a page detailing how to write test cases.](https://qa.php.net/write-test.php)
|
||||
|
||||
Submitting test scripts helps us to understand what functionality has changed.
|
||||
It is important for the stability and maintainability of PHP that tests are
|
||||
comprehensive.
|
||||
|
||||
Failure conditions of `zend_parse_parameters`, `ZEND_PARSE_PARAMETERS()` and
|
||||
similar functions should not be tested. These parameter parsing APIs are already
|
||||
extensively tested, and additional tests only complicate future modifications.
|
||||
|
||||
For newly created tests, a `--CREDITS--` section should no longer be included,
|
||||
as test authorship is already accurately tracked by Git. If multiple authors
|
||||
should be credited, the `Co-authored-by` tag in the commit message may be used.
|
||||
|
||||
## Writing documentation
|
||||
|
||||
There are two ways to contribute to the PHP manual. You can edit the manual and
|
||||
send patches anonymously via [the online editor](https://edit.php.net/), or you
|
||||
can check the XML source out from Subversion and edit that and build it
|
||||
[per the instructions on the documentation site](http://doc.php.net/tutorial/).
|
||||
Patches created that way should be sent to the
|
||||
[documentation mailing list](mailto:phpdoc@lists.php.net).
|
||||
|
||||
## Getting help
|
||||
|
||||
If you are having trouble contributing to PHP, or just want to talk to a human
|
||||
about what you're working on, you can contact us via the
|
||||
[internals mailing list](mailto:internals@lists.php.net), or the
|
||||
[documentation mailing list](mailto:phpdoc@lists.php.net) for documentation
|
||||
issues.
|
||||
|
||||
Although not a formal channel, you can also find a number of core developers on
|
||||
the #php.pecl channel on [EFnet](http://www.efnet.org/). Similarly, many
|
||||
documentation writers can be found on #php.doc. Windows development IRC channel
|
||||
is available at #winphp-dev on FreeNode.
|
||||
|
||||
## PHP source code directory structure
|
||||
|
||||
PHP source code also includes several files generated during development and
|
||||
several parts where maintenance is happening upstream in their respective
|
||||
locations.
|
||||
|
||||
```bash
|
||||
<php-src>/
|
||||
├─ .git/ # Git configuration and source directory
|
||||
├─ TSRM/ # Thread Safe Resource Manager
|
||||
└─ Zend/ # Zend Engine
|
||||
├─ asm/ # Bundled from src/asm in https://github.com/boostorg/context
|
||||
├─ zend_vm_execute.h # Generated by `Zend/zend_vm_gen.php`
|
||||
├─ zend_vm_opcodes.c # Generated by `Zend/zend_vm_gen.php`
|
||||
├─ zend_vm_opcodes.h # Generated by `Zend/zend_vm_gen.php`
|
||||
└─ ...
|
||||
├─ appveyor/ # Appveyor CI service files
|
||||
└─ build/ # *nix build system files
|
||||
├─ ax_*.m4 # https://github.com/autoconf-archive/autoconf-archive
|
||||
├─ config.guess # https://git.savannah.gnu.org/cgit/config.git
|
||||
├─ config.sub # https://git.savannah.gnu.org/cgit/config.git
|
||||
├─ libtool.m4 # https://git.savannah.gnu.org/cgit/libtool.git
|
||||
├─ ltmain.sh # https://git.savannah.gnu.org/cgit/libtool.git
|
||||
├─ pkg.m4 # https://gitlab.freedesktop.org/pkg-config/pkg-config
|
||||
├─ shtool # https://www.gnu.org/software/shtool/
|
||||
└─ ...
|
||||
├─ docs/ # PHP internals and repository documentation
|
||||
└─ ext/ # PHP core extensions
|
||||
└─ bcmath/
|
||||
├─ libbcmath/ # Forked and maintained in php-src
|
||||
└─ ...
|
||||
└─ curl/
|
||||
├─ sync-constants.php # The curl symbols checker
|
||||
└─ ...
|
||||
└─ date/
|
||||
└─ lib/ # Bundled datetime library https://github.com/derickr/timelib
|
||||
├─ parse_date.c # Generated by re2c 0.15.3
|
||||
├─ parse_iso_intervals.c # Generated by re2c 0.15.3
|
||||
└─ ...
|
||||
└─ ...
|
||||
└─ ffi/
|
||||
├─ ffi_parser.c # Generated by https://github.com/dstogov/llk
|
||||
└─ ...
|
||||
└─ fileinfo/
|
||||
├─ libmagic/ # Modified libmagic https://github.com/file/file
|
||||
├─ data_file.c # Generated by `ext/fileinfo/create_data_file.php`
|
||||
├─ libmagic.patch # Modifications patch from upstream libmagic
|
||||
├─ magicdata.patch # Modifications patch from upstream libmagic
|
||||
└─ ...
|
||||
└─ gd/
|
||||
├─ libgd/ # Bundled and modified GD library https://github.com/libgd/libgd
|
||||
└─ ...
|
||||
└─ mbstring/
|
||||
├─ libmbfl/ # Forked and maintained in php-src
|
||||
├─ unicode_data.h # Generated by `ext/mbstring/ucgendat/ucgendat.php`
|
||||
└─ ...
|
||||
└─ pcre/
|
||||
├─ pcre2lib/ # https://www.pcre.org/
|
||||
└─ ...
|
||||
└─ pdo_mysql/
|
||||
├─ php_pdo_mysql_sqlstate.h # Generated by `ext/pdo_mysql/get_error_codes.php`
|
||||
└─ ...
|
||||
└─ skeleton/ # Skeleton for developing new extensions with `ext/ext_skel.php`
|
||||
└─ ...
|
||||
└─ standard/
|
||||
└─ html_tables/
|
||||
├─ mappings/ # https://www.unicode.org/Public/MAPPINGS/
|
||||
└─ ...
|
||||
├─ credits_ext.h # Generated by `scripts/dev/credits`
|
||||
├─ credits_sapi.h # Generated by `scripts/dev/credits`
|
||||
├─ html_tables.h # Generated by `ext/standard/html_tables/html_table_gen.php`
|
||||
└─ ...
|
||||
└─ tokenizer/
|
||||
├─ tokenizer_data.c # Generated by `ext/tokenizer/tokenizer_data_gen.sh`
|
||||
└─ ...
|
||||
└─ zend_test # For testing internal APIs. Not needed for regular builds.
|
||||
└─ ...
|
||||
└─ zip/ # Bundled https://github.com/pierrejoye/php_zip
|
||||
└─ ...
|
||||
└─ ...
|
||||
└─ main/ # Binding that ties extensions, SAPIs, and engine together
|
||||
├─ streams/ # Streams layer subsystem
|
||||
├─ php_version.h # Generated by release managers using `configure`
|
||||
└─ ...
|
||||
├─ pear/ # PEAR installation
|
||||
└─ sapi/ # PHP SAPI modules
|
||||
└─ cli/
|
||||
├─ mime_type_map.h # Generated by `sapi/cli/generate_mime_type_map.php`
|
||||
└─ ...
|
||||
└─ ...
|
||||
├─ scripts/ # php-config, phpize and internal development scripts
|
||||
├─ tests/ # Core features tests
|
||||
├─ travis/ # Travis CI service files
|
||||
└─ win32/ # Windows build system files
|
||||
├─ cp_enc_map.c # Generated by `win32/cp_enc_map_gen.exe`
|
||||
└─ ...
|
||||
└─ ...
|
||||
```
|
||||
|
||||
## PHP internals
|
||||
|
||||
For information on PHP internal C functions see
|
||||
[References about Maintaining and Extending PHP](https://wiki.php.net/internals/references).
|
||||
Various external resources can be found on the web. A standard printed reference
|
||||
is the book "Extending and Embedding PHP" by Sara Golemon.
|
||||
|
||||
## PECL extensions
|
||||
|
||||
If you are fixing broken functionality in a [PECL](https://pecl.php.net)
|
||||
extension then create a bug or identify an existing bug at
|
||||
[bugs.php.net](https://bugs.php.net). A bug can be used to track the change
|
||||
progress and prevent your changes getting lost in the PHP mail archives. Some
|
||||
PECL extensions have their own bug tracker locations and different contributing
|
||||
procedures.
|
||||
|
||||
If your change is large then create a
|
||||
[Request for Comments (RFC)](https://wiki.php.net/rfc), discuss it with the
|
||||
extension maintainer, and discuss it on the development mailing list
|
||||
pecl-dev@lists.php.net depending on the extension. PECL mailing list
|
||||
subscription is explained on the
|
||||
[PECL support page](https://pecl.php.net/support.php).
|
||||
|
||||
Update any open bugs and add a link to the source of your change. Send the patch
|
||||
or pointer to the bug to pecl-dev@lists.php.net. Also CC the extension
|
||||
maintainer. Explain what has been changed by your patch. Test scripts should be
|
||||
included.
|
||||
|
||||
## Checklist for submitting contribution
|
||||
|
||||
- Read [Coding standards](/CODING_STANDARDS.md) before you start working.
|
||||
- Update git source just before running your final `diff` and before testing.
|
||||
- Add in-line comments and/or have external documentation ready. Use only
|
||||
`/* */` style comments, not `//`.
|
||||
- Create test scripts for use with `make test`.
|
||||
- Run `make test` to check your change doesn't break other features.
|
||||
- Rebuild PHP with `--enable-debug` which will show some kinds of memory errors
|
||||
and check the PHP and web server error logs after running your PHP tests.
|
||||
- Rebuild PHP with `--enable-zts` to check your change compiles and operates
|
||||
correctly in a thread safe PHP.
|
||||
- Review the change once more just before submitting it.
|
||||
|
||||
## What happens after submitting contribution?
|
||||
|
||||
If your change is easy to review and obviously has no side-effects, it might be
|
||||
committed relatively quickly.
|
||||
|
||||
Because PHP is a volunteer-driven effort more complex changes will require
|
||||
patience on your side. If you do not receive feedback in a few days, consider
|
||||
bumping. Before doing this think about these questions:
|
||||
|
||||
- Did I send the patch to the right mailing list?
|
||||
- Did I review the mailing list archives to see if these kind of changes had
|
||||
been discussed before?
|
||||
- Did I explain my change clearly?
|
||||
- Is my change too hard to review? Because of what factors?
|
||||
|
||||
## What happens when your contribution is applied?
|
||||
|
||||
Your name will likely be included in the Git commit log. If your change affects
|
||||
end users, a brief description and your name might be added to the [NEWS](/NEWS)
|
||||
file.
|
||||
|
||||
## Git commit rules
|
||||
|
||||
This section refers to contributors that have Git push access and make commit
|
||||
changes themselves. We'll assume you're basically familiar with Git, but feel
|
||||
free to post your questions on the mailing list. Please have a look at the more
|
||||
detailed [information on Git](https://git-scm.com/).
|
||||
|
||||
PHP is developed through the efforts of a large number of people. Collaboration
|
||||
is a Good Thing(tm), and Git lets us do this. Thus, following some basic rules
|
||||
with regards to Git usage will:
|
||||
|
||||
* Make everybody happier, especially those responsible for maintaining PHP
|
||||
itself.
|
||||
* Keep the changes consistently well documented and easily trackable.
|
||||
* Prevent some of those 'Oops' moments.
|
||||
* Increase the general level of good will on planet Earth.
|
||||
|
||||
Having said that, here are the organizational rules:
|
||||
|
||||
1. Respect other people working on the project.
|
||||
|
||||
2. Discuss any significant changes on the list before committing and get
|
||||
confirmation from the release manager for the given branch.
|
||||
|
||||
3. Look at [EXTENSIONS](/EXTENSIONS) file to see who is the primary maintainer
|
||||
of the code you want to contribute to.
|
||||
|
||||
4. If you "strongly disagree" about something another person did, don't start
|
||||
fighting publicly - take it up in private email.
|
||||
|
||||
5. If you don't know how to do something, ask first!
|
||||
|
||||
6. Test your changes before committing them. We mean it. Really. To do so use
|
||||
`make test`.
|
||||
|
||||
7. For development use the `--enable-debug` switch to avoid memory leaks and the
|
||||
`--enable-zts` switch to ensure your code handles TSRM correctly and doesn't
|
||||
break for those who need that.
|
||||
|
||||
Currently we have the following branches in use:
|
||||
|
||||
| Branch | |
|
||||
| --------- | --------- |
|
||||
| master | Active development branch for PHP 8.1, which is open for backwards incompatible changes and major internal API changes. |
|
||||
| PHP-8.0 | Is used to release the PHP 8.0.x series. This is a current stable version and is open for bugfixes only. |
|
||||
| PHP-7.4 | Is used to release the PHP 7.4.x series. This is a current stable version and is open for bugfixes only. |
|
||||
| PHP-7.3 | Is used to release the PHP 7.3.x series. This is a current stable version and is open for bugfixes only. |
|
||||
| PHP-7.2 | Is used to release the PHP 7.2.x series. This is an old stable version and is open for security fixes only. |
|
||||
| PHP-7.1 | This branch is closed. |
|
||||
| PHP-7.0 | This branch is closed. |
|
||||
| PHP-5.6 | This branch is closed. |
|
||||
| PHP-5.5 | This branch is closed. |
|
||||
| PHP-5.4 | This branch is closed. |
|
||||
| PHP-5.3 | This branch is closed. |
|
||||
| PHP-5.2 | This branch is closed. |
|
||||
| PHP-5.1 | This branch is closed. |
|
||||
| PHP-4.4 | This branch is closed. |
|
||||
| PHP-X.Y.Z | These branches are used for the release managers for tagging the releases, hence they are closed to the general public. |
|
||||
|
||||
The next few rules are more of a technical nature:
|
||||
|
||||
1. All non-security bugfix changes should first go to the lowest bugfix branch
|
||||
(i.e. 7.3) and then get merged up to all other branches. All security fixes
|
||||
should go to the lowest security fixes branch (i.e 7.2). If a change is not
|
||||
needed for later branches (i.e. fixes for features which were dropped from
|
||||
later branches) an empty merge should be done.
|
||||
|
||||
2. All news updates intended for public viewing, such as new features, bug
|
||||
fixes, improvements, etc., should go into the NEWS file of *any stable
|
||||
release* version with the given change. In other words, news about a bug fix
|
||||
which went into PHP-5.4, PHP-5.5 and master should be noted in both
|
||||
PHP-5.4/NEWS and PHP-5.5/NEWS but not master, which is not a public released
|
||||
version yet.
|
||||
|
||||
3. Do not commit multiple files and dump all messages in one commit. If you
|
||||
modified several unrelated files, commit each group separately and provide a
|
||||
nice commit message for each one. See example below.
|
||||
|
||||
4. Do write your commit message in such a way that it makes sense even without
|
||||
the corresponding diff. One should be able to look at it, and immediately
|
||||
know what was modified. Definitely include the function name in the message
|
||||
as shown below.
|
||||
|
||||
5. In your commit messages, keep each line shorter than 80 characters. And try
|
||||
to align your lines vertically, if they wrap. It looks bad otherwise.
|
||||
|
||||
6. If you modified a function that is callable from PHP, prepend PHP to the
|
||||
function name as shown below.
|
||||
|
||||
The format of the commit messages is pretty simple.
|
||||
|
||||
<max 79 characters short description>\n
|
||||
\n
|
||||
<long description, 79 chars per line>
|
||||
\n
|
||||
|
||||
An Example from the git project (commit 2b34e486bc):
|
||||
|
||||
pack-objects: Fix compilation with NO_PTHREDS
|
||||
|
||||
It looks like commit 99fb6e04 (pack-objects: convert to use parse_options(),
|
||||
2012-02-01) moved the #ifdef NO_PTHREDS around but hasn't noticed that the
|
||||
'arg' variable no longer is available.
|
||||
|
||||
If you fix some bugs, you should note the bug ID numbers in your commit message.
|
||||
Bug ID should be prefixed by `#`.
|
||||
|
||||
Example:
|
||||
|
||||
Fixed bug #14016 (pgsql notice handler double free crash bug.)
|
||||
|
||||
When you change the NEWS file for a bug fix, then please keep the bugs sorted in
|
||||
decreasing order under the fixed version.
|
||||
|
||||
## Copyright and license headers
|
||||
|
||||
New source code files should include the following header block:
|
||||
|
||||
```c
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
```
|
||||
|
||||
Thank you for contributing to PHP!
|
||||
@@ -0,0 +1,2 @@
|
||||
For the list of people who've put work into PHP, please see
|
||||
http://www.php.net/credits.php
|
||||
+189
-158
@@ -3,6 +3,7 @@
|
||||
|
||||
Maintenance legend
|
||||
------------------
|
||||
Supported: Someone is actually paid to look after this.
|
||||
Maintained: Someone actually looks after it.
|
||||
Odd Fixes: It has a maintainer but they don't have time to do
|
||||
much other than throw the odd patch in. See below.
|
||||
@@ -25,43 +26,103 @@ Status legend
|
||||
|
||||
== Server APIs ==
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: aolserver
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Unknown
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: apache
|
||||
PRIMARY MAINTAINER: Rasmus Lerdorf <rasmus@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: apache_hooks
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Unknown
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: apache2filter
|
||||
PRIMARY MAINTAINER: Aaron Bannert <aaron@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Experimental
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: apache2handler
|
||||
PRIMARY MAINTAINER: Aaron Bannert <aaron@php.net>, Ilia Alshanetsky <iliaa@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: caudium
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Unknown
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: cgi
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: cli
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net>, Edin Kadribasic <edink@php.net>
|
||||
MAINTENANCE: Xinchen Hui <laruence@php.net> (2013 - 2020)
|
||||
MAINTENANCE: Marcus Boerger <helly@php.net>, Edin Kadribasic <edink@php.net>
|
||||
STATUS: Working
|
||||
SINCE: 4.3.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: continuity
|
||||
PRIMARY MAINTAINER: Alex Leigh <aleigh@php.net>
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Unknown
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: embed
|
||||
PRIMARY MAINTAINER: Edin Kadribasic <edink@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: fpm
|
||||
PRIMARY MAINTAINER: Jakub Zelenka <bukka@php.net>
|
||||
PRIMARY MAINTAINER: Antony Dovgal <tony2001@php.net>, Jerome Loyet <fat@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: isapi
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: litespeed
|
||||
PRIMARY MAINTAINER: George Wang <gwang@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: phpdbg
|
||||
PRIMARY MAINTAINER: Joe Watkins <krakjoe@php.net>, Bob Weinand <bwoebi@php.net>
|
||||
EXTENSION: milter
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: 5.6
|
||||
STATUS: Unknown
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: nsapi
|
||||
PRIMARY MAINTAINER: Uwe Schindler <thetaphi@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: phttpd
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Unknown
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pi3web
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Unknown
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: roxen
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Unknown
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: thttpd
|
||||
PRIMARY MAINTAINER: Sascha Schumann <sascha@schumann.cx>
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: tux
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Unknown
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: webjames
|
||||
PRIMARY MAINTAINER: Alex Waugh <alex@alexwaugh.com>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Experimental
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -69,66 +130,70 @@ STATUS: 5.6
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: dba
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2002 - 2007)
|
||||
Christopher Jones <sixd@php.net> (2008 - 2013)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2011 - 2011)
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net>,Christopher Jones <sixd@php.net>, Pierre-Alain Joye <pajoye@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
COMMENT: DBM abstraction for db2, db3, db4, dbm, ndbm, gdbm, ini
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: interbase
|
||||
PRIMARY MAINTAINER: Ard Biesheuvel <ard@ard.nu>, Jouni Ahto <jouni.ahto@exdec.fi>
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mssql
|
||||
PRIMARY MAINTAINER: Frank M. Kromann <fmk@swwwing.com>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
COMMENT: Tested on phpts and isapi versions
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mysql
|
||||
PRIMARY MAINTAINER: Zak Greant <zak@mysql.com>, Georg Richter <georg@php.net>, Johannes Schlüter <johannes@php.net>, Ulf Wendel <uw@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mysqli
|
||||
PRIMARY MAINTAINER: Georg Richter <georg@php.net> (2003 - 2006)
|
||||
Andrey Hristov <andrey@php.net> (2003 - 2016)
|
||||
Johannes Schlüter <johannes@php.net> (2008 - 2014)
|
||||
Ulf Wendel <uw@php.net> (2007 - 2013)
|
||||
PRIMARY MAINTAINER: Georg Richter <georg@php.net>, Andrey Hristov <andrey@php.net>, Johannes Schlüter <johannes@php.net>, Ulf Wendel <uw@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mysqlnd
|
||||
PRIMARY MAINTAINER: Andrey Hristov <andrey@php.net> (2007 - 2017)
|
||||
Johannes Schlüter <johannes@php.net> (2008 - 2018)
|
||||
Ulf Wendel <uw@php.net> (2009 - 2011)
|
||||
PRIMARY MAINTAINER: Georg Richter <georg@php.net>, Andrey Hristov <andrey@php.net>, Johannes Schlüter <johannes@php.net>, Ulf Wendel <uw@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: oci8
|
||||
PRIMARY MAINTAINER: Christopher Jones <sixd@php.net> (2007 - 2017)
|
||||
Antony Dovgal <tony2001@php.net> (2003 - 2009)
|
||||
PRIMARY MAINTAINER: Christopher Jones <sixd@php.net>, Antony Dovgal <tony2001@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: odbc
|
||||
PRIMARY MAINTAINER: Daniel R. Kalowsky <kalowsky@php.net> (2000 - 2004)
|
||||
PRIMARY MAINTAINER: Daniel R. Kalowsky <kalowsky@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
COMMENT: Working
|
||||
COMMENT: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2004 - 2011)
|
||||
Wez Furlong <wez@php.net> (2004 - 2006)
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net>, Wez Furlong <wez@php.net>
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_dblib
|
||||
PRIMARY MAINTAINER: Adam Baratz <adambaratz@php.net> (2016 - 2017)
|
||||
MAINTENANCE: Maintained
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_firebird
|
||||
PRIMARY MAINTAINER: Lars Westermann <lwe@php.net> (2007 - 2007)
|
||||
PRIMARY MAINTAINER: Lars Westermann <lwe@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_mysql
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2004 - 2010)
|
||||
Johannes Schlüter <johannes@php.net> (2008 - 2014)
|
||||
Andrey Hristov <andrey@php.net> (2005 - 2015)
|
||||
Ulf Wendel <uw@php.net> (2008 - 2012)
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net>, Johannes Schlüter <johannes@php.net>, Andrey Hristov <andrey@php.net>, Ulf Wendel <uw@php.net>
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
@@ -140,89 +205,102 @@ STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_oci
|
||||
PRIMARY MAINTAINER: Christopher Jones <sixd@php.net> (2007 - 2017)
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_pgsql
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2004 - 2011)
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net>
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pdo_sqlite
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2005 - 2011)
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net>
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.1
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pgsql
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2002 - 2007)
|
||||
Yasuo Ohgaki <yohgaki@php.net> (2001 - 2015)
|
||||
PRIMARY MAINTAINER: Marcus Boerger <helly@php.net>, Yasuo Ohgaki <yohgaki@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
COMMENT: Use PostgreSQL 7.0.x or later. PostgreSQL 6.5.3 or less have fatal bug.
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sqlite
|
||||
PRIMARY MAINTAINER: Marcus Boerger <helly@php.net>, Wez Furlong <wez@php.net>, Ilia Alshanetsky <iliaa@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0 (Since 4.3.2 in PECL)
|
||||
COMMENT: Integrates SQLite 2 embeddable SQL database engine.
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sqlite3
|
||||
PRIMARY MAINTAINER: Scott MacVicar <scottmac@php.net> (2008 - 2011)
|
||||
Christoph M. Becker <cmb@php.net> (2016 - 2020)
|
||||
PRIMARY MAINTAINER: Scott MacVicar <scottmac@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
COMMENT: Integrates SQLite 3 embeddable SQL database engine.
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sybase_ct
|
||||
PRIMARY MAINTAINER: Timm Friebe <thekid@thekid.de>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
== XML extensions ==
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: dom
|
||||
PRIMARY MAINTAINER: Christian Stocker <chregu@php.net> (2003 - 2011)
|
||||
Rob Richards <rrichards@php.net> (2003 - 2012)
|
||||
Marcus Börger <helly@php.net> (2003 - 2006)
|
||||
PRIMARY MAINTAINER: Christian Stocker <chregu@php.net>, Rob Richards <rrichards@php.net>, Marcus Boerger <helly@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: simplexml
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2003 - 2008)
|
||||
PRIMARY MAINTAINER: Marcus Boerger <helly@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: soap
|
||||
PRIMARY MAINTAINER: Dmitry Stogov <dmitry@php.net> (2004 - 2018)
|
||||
PRIMARY MAINTAINER: Dmitry Stogov <dmitry@zend.com>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: wddx
|
||||
PRIMARY MAINTAINER: Andrei Zmievski <andrei@php.net>
|
||||
MAINTENANCE: Orphaned
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: xml
|
||||
PRIMARY MAINTAINER: Thies C. Arntzen <thies@thieso.net> (1999 - 2002)
|
||||
Rob Richards <rrichards@php.net> (2003 - 2013)
|
||||
PRIMARY MAINTAINER: Thies C. Arntzen <thies@thieso.net>, Rob Richards <rrichards@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: libxml
|
||||
PRIMARY MAINTAINER: Rob Richards <rrichards@php.net> (2003 - 2009)
|
||||
Christian Stocker <chregu@php.net> (2004 - 2011)
|
||||
PRIMARY MAINTAINER: Rob Richards <rrichards@php.net>, Christian Stocker <chregu@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: xmlreader
|
||||
PRIMARY MAINTAINER: Rob Richards <rrichards@php.net> (2004 - 2010)
|
||||
Christian Stocker <chregu@php.net> (2004 - 2004)
|
||||
PRIMARY MAINTAINER: Rob Richards <rrichards@php.net>, Christian Stocker <chregu@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: xmlrpc
|
||||
PRIMARY MAINTAINER: Dan Libby
|
||||
MAINTENANCE: Orphaned
|
||||
STATUS: Experimental
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: xmlwriter
|
||||
PRIMARY MAINTAINER: Rob Richards <rrichards@php.net> (2004 - 2010)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2005-2009)
|
||||
PRIMARY MAINTAINER: Rob Richards <rrichards@php.net>, Pierre-Alain Joye <pajoye@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: xsl
|
||||
PRIMARY MAINTAINER: Christian Stocker <chregu@php.net> (2003 - 2011)
|
||||
Rob Richards <rrichards@php.net> (2003 - 2010)
|
||||
PRIMARY MAINTAINER: Christian Stocker <chregu@php.net>, Rob Richards <rrichards@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0
|
||||
@@ -233,284 +311,237 @@ SINCE: 5.0
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: bcmath
|
||||
PRIMARY MAINTAINER: Andi Gutmans <andi@php.net> (2000 - 2004)
|
||||
MAINTENANCE: Unknown
|
||||
MAINTENANCE: Andi Gutmans <andi@zend.com>
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: bz2
|
||||
PRIMARY MAINTAINER: Hartmut Holzgraefe <hholzgra@php.net> (2004 - 2004)
|
||||
PRIMARY MAINTAINER: Hartmut Holzgraefe <hholzgra@php.net>
|
||||
MAINTENANCE: Odd Fixes
|
||||
STATUS: Working
|
||||
SINCE: 4.0.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: calendar
|
||||
PRIMARY MAINTAINER: Hartmut Holzgraefe <hholzgra@php.net> (2000 - 2004)
|
||||
PRIMARY MAINTAINER: Hartmut Holzgraefe <hholzgra@php.net>
|
||||
MAINTENANCE: Odd Fixes
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: com_dotnet
|
||||
PRIMARY MAINTAINER: Wez Furlong <wez@php.net> (2003 - 2005)
|
||||
Christoph M. Becker <cmb@php.net> (2018 - 2020)
|
||||
PRIMARY MAINTAINER: Wez Furlong <wez@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Windows
|
||||
SINCE: 5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: ctype
|
||||
PRIMARY MAINTAINER: Hartmut Holzgraefe <hholzgra@php.net> (2000 - 2004)
|
||||
PRIMARY MAINTAINER: Hartmut Holzgraefe <hholzgra@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: curl
|
||||
PRIMARY MAINTAINER: Sterling Hughes <sterling@php.net> (2000 - 2004)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2002 - 2011)
|
||||
Pierrick Charron <pierrick@php.net> (2010 - 2016)
|
||||
PRIMARY MAINTAINER: Sterling Hughes <sterling@php.net>, Ilia Alshanetsky <iliaa@php.net>, Pierrick Charron <pierrick@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: date
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@php.net> (2005 - 2018)
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: enchant
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: exif
|
||||
PRIMARY MAINTAINER: Kalle Sommer Nielsen <kalle@php.net> (2010 - 2018)
|
||||
PRIMARY MAINTAINER: Marcus Boerger <helly@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: ffi
|
||||
PRIMARY MAINTAINER: Dmitry Stogov <dmitry@php.net> (2018 - 2019)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Experimental
|
||||
SINCE: 7.4.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: fileinfo
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@php.net> (2004 - 2008)
|
||||
Anatol Belski <ab@php.net> (2012 - 2020)
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: filter
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@php.net> (2006 - 2006)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2006 - 2011)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2006 - 2011)
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@php.net>, Pierre-Alain Joye <pajoye@php.net>, Ilia Alshanetsky <iliaa@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: ftp
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
PRIMARY MAINTAINER: Stefan Esser <sesser@php.net>
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: gd
|
||||
PRIMARY MAINTAINER: Pierre-Alain Joye <pajoye@php.net> (2002 - 2016)
|
||||
Christoph M. Becker <cmb@php.net> (2015 - 2020)
|
||||
PRIMARY MAINTAINER: Pierre-Alain Joye <pajoye@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: gettext
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: gmp
|
||||
PRIMARY MAINTAINER: Stanislav Malyshev <stas@php.net> (2000 - 2019)
|
||||
Antony Dovgal <tony2001@php.net> (2005 - 2010)
|
||||
Nikita Popov <nikic@php.net> (2013 - 2020)
|
||||
PRIMARY MAINTAINER: Stanislav Malyshev <stas@php.net>, Antony Dovgal <tony2001@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.4
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: hash
|
||||
PRIMARY MAINTAINER: Sara Golemon <pollita@php.net> (2005 - 2017)
|
||||
Mike Wallner <mike@php.net> (2005 - 2013)
|
||||
Anatol Belski <ab@php.net> (2014 - 2018)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.1.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: iconv
|
||||
PRIMARY MAINTAINER: Moriyoshi Koizumi <moriyoshi@php.net> (2002 - 2010)
|
||||
PRIMARY MAINTAINER: Moriyoshi Koizumi <moriyoshi@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: intl
|
||||
PRIMARY MAINTAINER: Stanislav Malyshev <stas@php.net> (2008 - 2019)
|
||||
Anatol Belski <ab@php.net> (2017 - 2018)
|
||||
PRIMARY MAINTAINER: Stanislav Malyshev <stas@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: imap
|
||||
PRIMARY MAINTAINER: Chuck Hagenbuch <chuck@horde.org> (1999 - 2004)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2002 - 2010)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2008 - 2010)
|
||||
Bishop Bettini <bishop@php.net> (2018 - 2019)
|
||||
PRIMARY MAINTAINER: Chuck Hagenbuch <chuck@horde.org>, Ilia Alshanetsky <iliaa@php.net>, Pierre-Alain Joye <pajoye@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: json
|
||||
PRIMARY MAINTAINER: Jakub Zelenka <bukka@php.net> (2014 - 2018)
|
||||
MAINTENANCE: Maintained
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Odd fixes
|
||||
STATUS: Working
|
||||
SINCE: 5.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: ldap
|
||||
PRIMARY MAINTAINER: Stig Venaas <venaas@php.net> (2000 - 2002)
|
||||
Douglas Goldstein <cardoe@php.net> (2007 - 2007)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2008 - 2010)
|
||||
Côme Bernigaud <mcmic@php.net> (2015 - 2017)
|
||||
PRIMARY MAINTAINER: Stig Venaas <venaas@php.net>, Douglas Goldstein <cardoe@php.net>, Pierre-Alain Joye <pajoye@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mbstring
|
||||
PRIMARY MAINTAINER: Rui Hirokawa <hirokawa@php.net> (2001 - 2013)
|
||||
Nikita Popov <nikic@php.net> (2017 - 2020)
|
||||
PRIMARY MAINTAINER: Rui Hirokawa <hirokawa@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: opcache
|
||||
PRIMARY MAINTAINER: Dmitry Stogov <dmitry@php.net> (2013 - 2018)
|
||||
Xinchen Hui <laruence@php.net> (2013 - 2020)
|
||||
Nikita Popov <nikic@php.net> (2016 - 2020)
|
||||
EXTENSION: mcrypt
|
||||
PRIMARY MAINTAINER: Derick Rethans <derick@derickrethans.nl>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.5.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mhash
|
||||
PRIMARY MAINTAINER: Sascha Schumann <sascha@schumann.cx>, Scott MacVicar <scottmac@php.net>
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: mime_magic
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Deprecated
|
||||
STATUS: Experimental
|
||||
COMMENT: Use the fileinfo extension instead
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: ming
|
||||
PRIMARY MAINTAINER: Frank M. Kromann
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Experimental
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: openssl
|
||||
PRIMARY MAINTAINER: Wez Furlong <wez@php.net> (2001 - 2006)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2006 - 2011)
|
||||
Jakub Zelenka <bukka@php.net> (2015 - 2019)
|
||||
PRIMARY MAINTAINER: Wez Furlong <wez@php.net>, Pierre-Alain Joye <pajoye@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.4
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pcntl
|
||||
PRIMARY MAINTAINER: Arnaud Le Blanc <lbarnaud@php.net> (2008 - 2010)
|
||||
PRIMARY MAINTAINER: Arnaud Le Blanc <lbarnaud@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pcre
|
||||
PRIMARY MAINTAINER: Andrei Zmievski <andrei@php.net> (1999 - 2006)
|
||||
Nuno Lopes <nlopess@php.net> (2006 - 2009)
|
||||
PRIMARY MAINTAINER: Andrei Zmievski <andrei@php.net>, Nuno Lopes <nlopess@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: phar
|
||||
PRIMARY MAINTAINER: Greg Beaver <cellog@php.net> (2008 - 2009)
|
||||
Marcus Börger <helly@php.net> (2008 - 2008)
|
||||
Steph Fox <sfox@php.net> (2008 - 2008)
|
||||
Bishop Bettini <bishop@php.net> (2018 - 2019)
|
||||
PRIMARY MAINTAINER: Greg Beaver <cellog@php.net>, Marcus Börger <helly@php.net>, Steph Fox <sfox@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: posix
|
||||
PRIMARY MAINTAINER: Kristian Köhntopp <kris@koehntopp.de> (2000 - 2000)
|
||||
PRIMARY MAINTAINER: Kristian Köhntopp <kris@koehntopp.de>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: pspell
|
||||
PRIMARY MAINTAINER: Vlad Krupin <phpdevel@echospace.com> (2000 - 2004)
|
||||
PRIMARY MAINTAINER: Vlad Krupin <phpdevel@echospace.com>
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
SINCE: 4.0.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: readline
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: recode
|
||||
PRIMARY MAINTAINER: Kristian Köhntopp <kris@koehntopp.de>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: reflection
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2003 - 2009)
|
||||
Johannes Schlüter <johannes@php.net> (2006 - 2014)
|
||||
Nikita Popov <nikic@php.net> (2019 - 2020)
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net>, Johannes Schlüter <johannes@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: session
|
||||
PRIMARY MAINTAINER: Sascha Schumann <sascha@schumann.cx> (1999 - 2004)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2002 - 2012)
|
||||
PRIMARY MAINTAINER: Sascha Schumann <sascha@schumann.cx>, Ilia Alshanetsky <iliaa@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: shmop
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net> (2002 - 2008)
|
||||
PRIMARY MAINTAINER: Ilia Alshanetsky <iliaa@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.3
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: snmp
|
||||
PRIMARY MAINTAINER: Boris Lytochkin <lytboris@php.net> (2011 - 2013)
|
||||
Rasmus Lerdorf <rasmus@php.net> (1999 - 2002)
|
||||
Pierre-Alain Joye <pajoye@php.net> (2010 - 2011)
|
||||
PRIMARY MAINTAINER: Boris Lytochkin <lytboris@php.net>, Rasmus Lerdorf <rasmus@php.net>, Pierre-Alain Joye <pajoye@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sockets
|
||||
PRIMARY MAINTAINER: Chris Vandomelen <chrisv@b0rked.dhs.org> (2000 - 2000)
|
||||
PRIMARY MAINTAINER: Chris Vandomelen <chrisv@b0rked.dhs.org>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 4.0.2
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sodium
|
||||
PRIMARY MAINTAINER: Frank Denis <jedisct1@php.net> (2017 - 2017)
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 7.2.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: spl
|
||||
PRIMARY MAINTAINER: Marcus Börger <helly@php.net> (2003 - 2009)
|
||||
Etienne Kneuss <colder@php.net> (2008 - 2014)
|
||||
PRIMARY MAINTAINER: Marcus Boerger <helly@php.net>, Etienne Kneuss <colder@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
SINCE: 5.0.0
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sysvmsg
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sysvsem
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: sysvshm
|
||||
PRIMARY MAINTAINER: Unknown
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: tidy
|
||||
PRIMARY MAINTAINER: John Coggeshall <john@php.net> (2003 - 2006)
|
||||
Ilia Alshanetsky <iliaa@php.net> (2003 - 2009)
|
||||
Nuno Lopes <nlopess@php.net> (2006 - 2012)
|
||||
PRIMARY MAINTAINER: John Coggeshall <john@php.net>, Ilia Alshanetsky <iliaa@php.net>, Nuno Lopes <nlopess@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: tokenizer
|
||||
PRIMARY MAINTAINER: Andrei Zmievski <andrei@php.net> (2002 - 2002)
|
||||
Nikita Popov <nikic@php.net> (2013 - 2020)
|
||||
MAINTENANCE: Maintained
|
||||
PRIMARY MAINTAINER: Andrei Zmievski <andrei@php.net>
|
||||
MAINTENANCE: Unknown
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: zip
|
||||
PRIMARY MAINTAINER: Pierre-Alain Joye <pajoye@php.net> (2006 - 2011)
|
||||
Remi Collet <remi@php.net> (2013-2020)
|
||||
PRIMARY MAINTAINER: Pierre-Alain Joye <pajoye@php.net>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
EXTENSION: zlib
|
||||
PRIMARY MAINTAINER: Stefan Roehrich <sr@linux.de> (1999 - 2003)
|
||||
PRIMARY MAINTAINER: Stefan Roehrich <sr@linux.de>
|
||||
MAINTENANCE: Maintained
|
||||
STATUS: Working
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -1,7 +1,7 @@
|
||||
--------------------------------------------------------------------
|
||||
--------------------------------------------------------------------
|
||||
The PHP License, version 3.01
|
||||
Copyright (c) 1999 - 2021 The PHP Group. All rights reserved.
|
||||
--------------------------------------------------------------------
|
||||
Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted provided that the following conditions
|
||||
@@ -9,22 +9,22 @@ are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
|
||||
3. The name "PHP" must not be used to endorse or promote products
|
||||
derived from this software without prior written permission. For
|
||||
written permission, please contact group@php.net.
|
||||
|
||||
|
||||
4. Products derived from this software may not be called "PHP", nor
|
||||
may "PHP" appear in their name, without prior written permission
|
||||
from group@php.net. You may indicate that your software works in
|
||||
conjunction with PHP by saying "Foo for PHP" instead of calling
|
||||
it "PHP Foo" or "phpfoo"
|
||||
|
||||
|
||||
5. The PHP Group may publish revised and/or new versions of the
|
||||
license from time to time. Each version will be given a
|
||||
distinguishing version number.
|
||||
@@ -41,27 +41,27 @@ are met:
|
||||
"This product includes PHP software, freely available from
|
||||
<http://www.php.net/software/>".
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
|
||||
THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
|
||||
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
|
||||
DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
--------------------------------------------------------------------
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals on behalf of the PHP Group.
|
||||
|
||||
The PHP Group can be contacted via Email at group@php.net.
|
||||
|
||||
For more information on the PHP Group and the PHP project,
|
||||
For more information on the PHP Group and the PHP project,
|
||||
please see <http://www.php.net>.
|
||||
|
||||
PHP includes the Zend Engine, freely available at
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
#
|
||||
# Zend
|
||||
#
|
||||
|
||||
$(builddir)/zend_language_scanner.lo: $(srcdir)/zend_language_parser.h
|
||||
$(builddir)/zend_ini_scanner.lo: $(srcdir)/zend_ini_parser.h
|
||||
|
||||
$(srcdir)/zend_language_scanner.c: $(srcdir)/zend_language_scanner.l
|
||||
@(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date --case-inverted -cbdFt Zend/zend_language_scanner_defs.h -oZend/zend_language_scanner.c Zend/zend_language_scanner.l)
|
||||
|
||||
$(srcdir)/zend_language_parser.h: $(srcdir)/zend_language_parser.c
|
||||
$(srcdir)/zend_language_parser.c: $(srcdir)/zend_language_parser.y
|
||||
@$(YACC) -p zend -v -d $(srcdir)/zend_language_parser.y -o $@
|
||||
|
||||
$(srcdir)/zend_ini_parser.h: $(srcdir)/zend_ini_parser.c
|
||||
$(srcdir)/zend_ini_parser.c: $(srcdir)/zend_ini_parser.y
|
||||
@$(YACC) -p ini_ -v -d $(srcdir)/zend_ini_parser.y -o $@
|
||||
|
||||
$(srcdir)/zend_ini_scanner.c: $(srcdir)/zend_ini_scanner.l
|
||||
@(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date --case-inverted -cbdFt Zend/zend_ini_scanner_defs.h -oZend/zend_ini_scanner.c Zend/zend_ini_scanner.l)
|
||||
|
||||
$(builddir)/zend_indent.lo $(builddir)/zend_highlight.lo $(builddir)/zend_compile.lo: $(srcdir)/zend_language_parser.h
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
#
|
||||
# LCOV
|
||||
#
|
||||
|
||||
LCOV_INCLUDE="."
|
||||
|
||||
lcov: lcov-html
|
||||
|
||||
lcov-test: lcov-clean-data test
|
||||
|
||||
php_lcov.info: lcov-test
|
||||
@echo "Generating data for $@"
|
||||
@rm -rf lcov_data/
|
||||
@$(mkinstalldirs) lcov_data/
|
||||
@echo
|
||||
-@files=`find . -name \*.gcda -o -name \*.gcno -o -name \*.da -o -name \*.h | sed -e 's/^\.\///' | sed -e 's/\.gcda//g' -e 's/\.gcno//g' -e 's/\.da//g' | $(EGREP) $(LCOV_INCLUDE) | uniq` ;\
|
||||
for x in $$files; do \
|
||||
echo -n . ;\
|
||||
y=`echo $$x | sed -e 's!\.libs/!!'`; \
|
||||
dir=lcov_data/`dirname $$x`; \
|
||||
test -d "$$dir" || $(mkinstalldirs) "$$dir"; \
|
||||
if test -f "$(top_srcdir)/$$y.c"; then \
|
||||
ln -f -s $(top_srcdir)/$$y.c lcov_data/$$y.c; \
|
||||
fi; \
|
||||
if test -f "$(top_srcdir)/$$y.h"; then \
|
||||
ln -f -s $(top_srcdir)/$$y.h lcov_data/$$y.h; \
|
||||
fi; \
|
||||
if test -f "$(top_srcdir)/$$y.re"; then \
|
||||
ln -f -s $(top_srcdir)/$$y.re lcov_data/$$y.re; \
|
||||
fi; \
|
||||
if test -f "$(top_srcdir)/$$y.y"; then \
|
||||
ln -f -s $(top_srcdir)/$$y.y lcov_data/$$y.y; \
|
||||
fi; \
|
||||
if test -f "$(top_srcdir)/$$y.l"; then \
|
||||
ln -f -s $(top_srcdir)/$$y.l lcov_data/$$y.l; \
|
||||
fi; \
|
||||
if test -f "$(top_srcdir)/$$y"; then \
|
||||
ln -f -s $(top_srcdir)/$$y lcov_data/$$y; \
|
||||
fi; \
|
||||
if test -f "$(top_builddir)/$$y.c"; then \
|
||||
ln -f -s $(top_builddir)/$$y.c lcov_data/$$y.c; \
|
||||
fi; \
|
||||
if test -f "$$x.gcno"; then \
|
||||
cp $$x.gcno lcov_data/$$y.gcno ; \
|
||||
fi; \
|
||||
if test -f "$$x.gcda"; then \
|
||||
cp $$x.gcda lcov_data/$$y.gcda ; \
|
||||
fi; \
|
||||
if test -f "$$x.da"; then \
|
||||
cp $$x.da lcov_data/$$y.da ; \
|
||||
fi; \
|
||||
if test -f "$$x.bb"; then \
|
||||
cp $$x.bb lcov_data/$$y.bb ; \
|
||||
fi; \
|
||||
if test -f "$$x.bbg"; then \
|
||||
cp $$x.bbg lcov_data/$$y.bbg ; \
|
||||
fi; \
|
||||
done; \
|
||||
for dir in ext/bcmath/libbcmath ext/fileinfo/libmagic ext/gd/libgd ext/mbstring/libmbfl ext/mbstring/oniguruma ext/pcre/pcrelib ext/pdo_sqlite/libsqlite ext/sqlite/libsqlite ext/sqlite3/libsqlite ext/xmlrpc/libxmlrpc ext/zip/lib; do \
|
||||
if test -d lcov_data/$$dir; then \
|
||||
rm -rf lcov_data/$$dir ; \
|
||||
fi; \
|
||||
done
|
||||
@echo
|
||||
@echo "Generating $@"
|
||||
@$(LTP) --directory lcov_data/ --capture --base-directory=lcov_data --output-file $@
|
||||
|
||||
lcov-html: php_lcov.info
|
||||
@echo "Generating lcov HTML"
|
||||
@$(LTP_GENHTML) --legend --output-directory lcov_html/ --title "PHP Code Coverage" php_lcov.info
|
||||
|
||||
lcov-clean:
|
||||
rm -f php_lcov.info
|
||||
rm -rf lcov_data/
|
||||
rm -rf lcov_html/
|
||||
|
||||
lcov-clean-data:
|
||||
@find . -name \*.gcda -o -name \*.da -o -name \*.bbg? | xargs rm -f
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
mkinstalldirs = $(top_srcdir)/build/shtool mkdir -p
|
||||
INSTALL = $(top_srcdir)/build/shtool install -c
|
||||
INSTALL_DATA = $(INSTALL) -m 644
|
||||
|
||||
DEFS = -DPHP_ATOM_INC -I$(top_builddir)/include -I$(top_builddir)/main -I$(top_srcdir)
|
||||
COMMON_FLAGS = $(DEFS) $(INCLUDES) $(EXTRA_INCLUDES) $(CPPFLAGS) $(PHP_FRAMEWORKPATH)
|
||||
|
||||
all: $(all_targets)
|
||||
@echo
|
||||
@echo "Build complete."
|
||||
@echo "Don't forget to run 'make test'."
|
||||
@echo
|
||||
|
||||
build-modules: $(PHP_MODULES) $(PHP_ZEND_EX)
|
||||
|
||||
build-binaries: $(PHP_BINARIES)
|
||||
|
||||
libphp$(PHP_MAJOR_VERSION).la: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS)
|
||||
$(LIBTOOL) --mode=link $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -rpath $(phptempdir) $(EXTRA_LDFLAGS) $(LDFLAGS) $(PHP_RPATHS) $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@
|
||||
-@$(LIBTOOL) --silent --mode=install cp $@ $(phptempdir)/$@ >/dev/null 2>&1
|
||||
|
||||
libs/libphp$(PHP_MAJOR_VERSION).bundle: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS)
|
||||
$(CC) $(MH_BUNDLE_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) $(LDFLAGS) $(EXTRA_LDFLAGS) $(PHP_GLOBAL_OBJS:.lo=.o) $(PHP_SAPI_OBJS:.lo=.o) $(PHP_FRAMEWORKS) $(EXTRA_LIBS) $(ZEND_EXTRA_LIBS) -o $@ && cp $@ libs/libphp$(PHP_MAJOR_VERSION).so
|
||||
|
||||
install: $(all_targets) $(install_targets)
|
||||
|
||||
install-sapi: $(OVERALL_TARGET)
|
||||
@echo "Installing PHP SAPI module: $(PHP_SAPI)"
|
||||
-@$(mkinstalldirs) $(INSTALL_ROOT)$(bindir)
|
||||
-@if test ! -r $(phptempdir)/libphp$(PHP_MAJOR_VERSION).$(SHLIB_DL_SUFFIX_NAME); then \
|
||||
for i in 0.0.0 0.0 0; do \
|
||||
if test -r $(phptempdir)/libphp$(PHP_MAJOR_VERSION).$(SHLIB_DL_SUFFIX_NAME).$$i; then \
|
||||
$(LN_S) $(phptempdir)/libphp$(PHP_MAJOR_VERSION).$(SHLIB_DL_SUFFIX_NAME).$$i $(phptempdir)/libphp$(PHP_MAJOR_VERSION).$(SHLIB_DL_SUFFIX_NAME); \
|
||||
break; \
|
||||
fi; \
|
||||
done; \
|
||||
fi
|
||||
@$(INSTALL_IT)
|
||||
|
||||
install-binaries: build-binaries $(install_binary_targets)
|
||||
|
||||
install-modules: build-modules
|
||||
@test -d modules && \
|
||||
$(mkinstalldirs) $(INSTALL_ROOT)$(EXTENSION_DIR)
|
||||
@echo "Installing shared extensions: $(INSTALL_ROOT)$(EXTENSION_DIR)/"
|
||||
@rm -f modules/*.la >/dev/null 2>&1
|
||||
@$(INSTALL) modules/* $(INSTALL_ROOT)$(EXTENSION_DIR)
|
||||
|
||||
install-headers:
|
||||
-@if test "$(INSTALL_HEADERS)"; then \
|
||||
for i in `echo $(INSTALL_HEADERS)`; do \
|
||||
i=`$(top_srcdir)/build/shtool path -d $$i`; \
|
||||
paths="$$paths $(INSTALL_ROOT)$(phpincludedir)/$$i"; \
|
||||
done; \
|
||||
$(mkinstalldirs) $$paths && \
|
||||
echo "Installing header files: $(INSTALL_ROOT)$(phpincludedir)/" && \
|
||||
for i in `echo $(INSTALL_HEADERS)`; do \
|
||||
if test "$(PHP_PECL_EXTENSION)"; then \
|
||||
src=`echo $$i | $(SED) -e "s#ext/$(PHP_PECL_EXTENSION)/##g"`; \
|
||||
else \
|
||||
src=$$i; \
|
||||
fi; \
|
||||
if test -f "$(top_srcdir)/$$src"; then \
|
||||
$(INSTALL_DATA) $(top_srcdir)/$$src $(INSTALL_ROOT)$(phpincludedir)/$$i; \
|
||||
elif test -f "$(top_builddir)/$$src"; then \
|
||||
$(INSTALL_DATA) $(top_builddir)/$$src $(INSTALL_ROOT)$(phpincludedir)/$$i; \
|
||||
else \
|
||||
(cd $(top_srcdir)/$$src && $(INSTALL_DATA) *.h $(INSTALL_ROOT)$(phpincludedir)/$$i; \
|
||||
cd $(top_builddir)/$$src && $(INSTALL_DATA) *.h $(INSTALL_ROOT)$(phpincludedir)/$$i) 2>/dev/null || true; \
|
||||
fi \
|
||||
done; \
|
||||
fi
|
||||
|
||||
PHP_TEST_SETTINGS = -d 'open_basedir=' -d 'output_buffering=0' -d 'memory_limit=-1'
|
||||
PHP_TEST_SHARED_EXTENSIONS = ` \
|
||||
if test "x$(PHP_MODULES)" != "x"; then \
|
||||
for i in $(PHP_MODULES)""; do \
|
||||
. $$i; $(top_srcdir)/build/shtool echo -n -- " -d extension=$$dlname"; \
|
||||
done; \
|
||||
fi; \
|
||||
if test "x$(PHP_ZEND_EX)" != "x"; then \
|
||||
for i in $(PHP_ZEND_EX)""; do \
|
||||
. $$i; $(top_srcdir)/build/shtool echo -n -- " -d $(ZEND_EXT_TYPE)=$(top_builddir)/modules/$$dlname"; \
|
||||
done; \
|
||||
fi`
|
||||
PHP_DEPRECATED_DIRECTIVES_REGEX = '^(magic_quotes_(gpc|runtime|sybase)?|(zend_)?extension(_debug)?(_ts)?)[\t\ ]*='
|
||||
|
||||
test: all
|
||||
@if test ! -z "$(PHP_EXECUTABLE)" && test -x "$(PHP_EXECUTABLE)"; then \
|
||||
INI_FILE=`$(PHP_EXECUTABLE) -d 'display_errors=stderr' -r 'echo php_ini_loaded_file();' 2> /dev/null`; \
|
||||
if test "$$INI_FILE"; then \
|
||||
$(EGREP) -h -v $(PHP_DEPRECATED_DIRECTIVES_REGEX) "$$INI_FILE" > $(top_builddir)/tmp-php.ini; \
|
||||
else \
|
||||
echo > $(top_builddir)/tmp-php.ini; \
|
||||
fi; \
|
||||
INI_SCANNED_PATH=`$(PHP_EXECUTABLE) -d 'display_errors=stderr' -r '$$a = explode(",\n", trim(php_ini_scanned_files())); echo $$a[0];' 2> /dev/null`; \
|
||||
if test "$$INI_SCANNED_PATH"; then \
|
||||
INI_SCANNED_PATH=`$(top_srcdir)/build/shtool path -d $$INI_SCANNED_PATH`; \
|
||||
$(EGREP) -h -v $(PHP_DEPRECATED_DIRECTIVES_REGEX) "$$INI_SCANNED_PATH"/*.ini >> $(top_builddir)/tmp-php.ini; \
|
||||
fi; \
|
||||
TEST_PHP_EXECUTABLE=$(PHP_EXECUTABLE) \
|
||||
TEST_PHP_SRCDIR=$(top_srcdir) \
|
||||
CC="$(CC)" \
|
||||
$(PHP_EXECUTABLE) -n -c $(top_builddir)/tmp-php.ini $(PHP_TEST_SETTINGS) $(top_srcdir)/run-tests.php -n -c $(top_builddir)/tmp-php.ini -d extension_dir=$(top_builddir)/modules/ $(PHP_TEST_SHARED_EXTENSIONS) $(TESTS); \
|
||||
TEST_RESULT_EXIT_CODE=$$?; \
|
||||
rm $(top_builddir)/tmp-php.ini; \
|
||||
exit $$TEST_RESULT_EXIT_CODE; \
|
||||
else \
|
||||
echo "ERROR: Cannot run tests without CLI sapi."; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
find . -name \*.gcno -o -name \*.gcda | xargs rm -f
|
||||
find . -name \*.lo -o -name \*.o | xargs rm -f
|
||||
find . -name \*.la -o -name \*.a | xargs rm -f
|
||||
find . -name \*.so | xargs rm -f
|
||||
find . -name .libs -a -type d|xargs rm -rf
|
||||
rm -f libphp$(PHP_MAJOR_VERSION).la $(SAPI_CLI_PATH) $(OVERALL_TARGET) modules/* libs/*
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile config.cache config.log config.status Makefile.objects Makefile.fragments libtool main/php_config.h stamp-h sapi/apache/libphp$(PHP_MAJOR_VERSION).module buildmk.stamp
|
||||
$(EGREP) define'.*include/php' $(top_srcdir)/configure | $(SED) 's/.*>//'|xargs rm -f
|
||||
|
||||
.PHONY: all clean install distclean test
|
||||
.NOEXPORT:
|
||||
@@ -1,139 +1,235 @@
|
||||
PHP NEWS
|
||||
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||
?? ??? ????, PHP 8.1.0alpha1
|
||||
?? ??? 201?, PHP 5.5.0 Alpha 3
|
||||
|
||||
- General improvements:
|
||||
. Fixed bug #63874 (Segfault if php_strip_whitespace has heredoc). (Pierrick)
|
||||
. Fixed bug #63822 (Crash when using closures with ArrayAccess).
|
||||
(Nikita Popov)
|
||||
. Add Generator::throw() method. (Nikita Popov)
|
||||
. Bug #23955: allow specifying Max-Age attribute in setcookie() (narfbg, Lars)
|
||||
. Bug #52126: timestamp for mail.log (Martin Jansen, Lars)
|
||||
|
||||
- mysqlnd
|
||||
. Fixed return value of mysqli_stmt_affected_rows() in the time after
|
||||
prepare() and before execute(). (Andrey)
|
||||
|
||||
- cURL:
|
||||
. Added new functions curl_escape, curl_multi_setopt, curl_multi_strerror
|
||||
curl_pause, curl_reset, curl_share_close, curl_share_init,
|
||||
curl_share_setopt curl_strerror and curl_unescape. (Pierrick)
|
||||
. Addes new curl options CURLOPT_TELNETOPTIONS, CURLOPT_GSSAPI_DELEGATION,
|
||||
CURLOPT_ACCEPTTIMEOUT_MS, CURLOPT_SSL_OPTIONS, CURLOPT_TCP_KEEPALIVE,
|
||||
CURLOPT_TCP_KEEPIDLE and CURLOPT_TCP_KEEPINTVL. (Pierrick)
|
||||
|
||||
18 Dec 2012, PHP 5.5.0 Alpha 2
|
||||
|
||||
- General improvements:
|
||||
. Added systemtap support by enabling systemtap compatible dtrace probes on
|
||||
linux. (David Soria Parra)
|
||||
. Added support for using empty() on the result of function calls and
|
||||
other expressions (https://wiki.php.net/rfc/empty_isset_exprs).
|
||||
(Nikita Popov)
|
||||
. Optimized access to temporary and compiled VM variables. 8% less memory
|
||||
reads. (Dmitry)
|
||||
. The VM stacks for passing function arguments and syntaticaly nested calls
|
||||
were merged into a single stack. The stack size needed for op_array
|
||||
execution is calculated at compile time and preallocated at once. As result
|
||||
all the stack push operatins don't require checks for stack overflow
|
||||
any more. (Dmitry)
|
||||
|
||||
- MySQL
|
||||
. This extension is now deprecated, and deprecation warnings will be generated
|
||||
when connections are established to databases via mysql_connect(),
|
||||
mysql_pconnect(), or through implicit connection: use MySQLi or PDO_MySQL
|
||||
instead (https://wiki.php.net/rfc/mysql_deprecation). (Adam)
|
||||
|
||||
- Fileinfo:
|
||||
. Fixed bug #63590 (Different results in TS and NTS under Windows).
|
||||
(Anatoliy)
|
||||
|
||||
- Apache2 Handler SAPI:
|
||||
. Enabled Apache 2.4 configure option for Windows (Pierre, Anatoliy)
|
||||
|
||||
13 Nov 2012, PHP 5.5.0 Alpha 1
|
||||
|
||||
- General improvements:
|
||||
. Added generators and coroutines (https://wiki.php.net/rfc/generators).
|
||||
(Nikita Popov)
|
||||
. Added "finally" keyword (https://wiki.php.net/rfc/finally). (Laruence)
|
||||
. Add simplified password hashing API
|
||||
(https://wiki.php.net/rfc/password_hash). (Anthony Ferrara)
|
||||
. Added support for list in foreach (https://wiki.php.net/rfc/foreachlist).
|
||||
(Laruence)
|
||||
. Added support for using empty() on the result of function calls and
|
||||
other expressions (https://wiki.php.net/rfc/empty_isset_exprs).
|
||||
(Nikita Popov)
|
||||
. Added support for constant array/string dereferencing. (Laruence)
|
||||
. Improve set_exception_handler while doing reset.(Laruence)
|
||||
. Remove php_logo_guid(), php_egg_logo_guid(), php_real_logo_guid(),
|
||||
zend_logo_guid(). (Adnrew Faulds)
|
||||
. Drop Windows XP and 2003 support. (Pierre)
|
||||
|
||||
- Calendar:
|
||||
. Fixed bug #54254 (cal_from_jd returns month = 6 when there is only one Adar)
|
||||
(Stas, Eitan Mosenkis)
|
||||
|
||||
- Core:
|
||||
. Fixed inclusion order for phpize builds on Windows. (cmb)
|
||||
. Added missing hashtable insertion APIs for arr/obj/ref. (Sara)
|
||||
. Fixed bug #75474 (function scope static variables are not bound to a unique
|
||||
function). (Nikita)
|
||||
. Fixed bug #53826 (__callStatic fired in base class through a parent call if
|
||||
the method is private). (Nikita)
|
||||
. Implemented FR #77372 (Relative file path is removed from uploaded file).
|
||||
(Björn Tantau)
|
||||
. Fixed bug #81076 (incorrect debug info on Closures with implicit binds).
|
||||
(krakjoe)
|
||||
|
||||
- Standard:
|
||||
. Fixed bug #77627 (method_exists on Closure::__invoke inconsistency).
|
||||
(krakjoe)
|
||||
. Added boolval(). (Jille Timmermans)
|
||||
. Added "Z" option to pack/unpack. (Gustavo)
|
||||
. Implemented FR #60738 (Allow 'set_error_handler' to handle NULL).
|
||||
(Laruence, Nikita Popov)
|
||||
. Added optional second argument for assert() to specify custom message. Patch
|
||||
by Lonny Kapelushnik (lonny@lonnylot.com). (Lars)
|
||||
. Fixed bug #18556 (Engine uses locale rules to handle class names). (Stas)
|
||||
. Fixed bug #61681 (Malformed grammar). (Nikita Popov, Etienne, Laruence)
|
||||
. Fixed bug #61038 (unpack("a5", "str\0\0") does not work as expected).
|
||||
(srgoogleguy, Gustavo)
|
||||
. Return previous handler when passing NULL to set_error_handler and
|
||||
set_exception_handler. (Nikita Popov)
|
||||
|
||||
- Date:
|
||||
. Fixed bug #52480 (Incorrect difference using DateInterval) (Derick)
|
||||
. Fixed bug #62326 (date_diff() function returns false result) (Derick)
|
||||
. Fixed bug #64992 (dst not handled past 2038) (Derick)
|
||||
. Fixed bug #65003 (Wrong date diff) (Derick)
|
||||
. Fixed bug #66545 (DateTime. diff returns negative values) (Derick)
|
||||
. Fixed bug #68503 (date_diff on two dates with timezone set localised
|
||||
returns wrong results) (Derick)
|
||||
. Fixed bug #69806 (Incorrect date from timestamp) (Derick)
|
||||
. Fixed bug #71700 (Extra day on diff between begin and end of march 2016)
|
||||
(Derick)
|
||||
. Fixed bug #71826 (DateTime::diff confuse on timezone 'Asia/Tokyo') (Derick)
|
||||
. Fixed bug #73460 (Datetime add not realising it already applied DST
|
||||
change) (Derick)
|
||||
. Fixed bug #74173 (DateTimeImmutable::getTimestamp() triggers DST switch in
|
||||
incorrect time) (Derick)
|
||||
. Fixed bug #74274 (Handling DST transitions correctly) (Derick)
|
||||
. Fixed bug #74524 (Date diff is bad calculated, in same time zone) (Derick)
|
||||
. Fixed bug #75167 (DateTime::add does only care about backward DST
|
||||
transition, not forward) (Derick)
|
||||
. Fixed bug #76032 (DateTime->diff having issues with leap days for
|
||||
timezones ahead of UTC) (Derick)
|
||||
. Fixed bug #76374 (Date difference varies according day time) (Derick)
|
||||
. Fixed bug #77571 (DateTime's diff DateInterval incorrect in timezones from
|
||||
UTC+01:00 to UTC+12:00 (Derick)
|
||||
. Fixed bug #78452 (diff makes wrong in hour for Asia/Tehran) (Derick)
|
||||
. Fixed bug #79452 (DateTime::diff() generates months differently between
|
||||
time zones) (Derick)
|
||||
. Fixed bug #79698 (timelib mishandles future timestamps (triggered by 'zic
|
||||
-b slim')) (Derick)
|
||||
. Fixed bug #79716 (Invalid date time created (with day "00")) (Derick)
|
||||
. Fixed bug #80610 (DateTime calculate wrong with DateInterval) (Derick)
|
||||
. Fixed bug #80664 (DateTime objects behave incorrectly around DST
|
||||
transition) (Derick)
|
||||
. Fixed bug #80913 (DateTime(Immutable)::sub around DST yield incorrect
|
||||
time) (Derick)
|
||||
- cURL:
|
||||
. Added support for CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPT_APPEND,
|
||||
CURLOPT_DIRLISTONLY, CURLOPT_NEW_DIRECTORY_PERMS, CURLOPT_NEW_FILE_PERMS,
|
||||
CURLOPT_NETRC_FILE, CURLOPT_PREQUOTE, CURLOPT_KRBLEVEL, CURLOPT_MAXFILESIZE,
|
||||
CURLOPT_FTP_ACCOUNT, CURLOPT_COOKIELIST, CURLOPT_IGNORE_CONTENT_LENGTH,
|
||||
CURLOPT_CONNECT_ONLY, CURLOPT_LOCALPORT, CURLOPT_LOCALPORTRANGE,
|
||||
CURLOPT_FTP_ALTERNATIVE_TO_USER, CURLOPT_SSL_SESSIONID_CACHE,
|
||||
CURLOPT_FTP_SSL_CCC, CURLOPT_HTTP_CONTENT_DECODING,
|
||||
CURLOPT_HTTP_TRANSFER_DECODING, CURLOPT_PROXY_TRANSFER_MODE,
|
||||
CURLOPT_ADDRESS_SCOPE, CURLOPT_CRLFILE, CURLOPT_ISSUERCERT,
|
||||
CURLOPT_USERNAME, CURLOPT_PASSWORD, CURLOPT_PROXYUSERNAME,
|
||||
CURLOPT_PROXYPASSWORD, CURLOPT_NOPROXY, CURLOPT_SOCKS5_GSSAPI_NEC,
|
||||
CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOPT_TFTP_BLKSIZE,
|
||||
CURLOPT_SSH_KNOWNHOSTS, CURLOPT_FTP_USE_PRET, CURLOPT_MAIL_FROM,
|
||||
CURLOPT_MAIL_RCPT, CURLOPT_RTSP_CLIENT_CSEQ, CURLOPT_RTSP_SERVER_CSEQ,
|
||||
CURLOPT_RTSP_SESSION_ID, CURLOPT_RTSP_STREAM_URI, CURLOPT_RTSP_TRANSPORT,
|
||||
CURLOPT_RTSP_REQUEST, CURLOPT_RESOLVE, CURLOPT_ACCEPT_ENCODING,
|
||||
CURLOPT_TRANSFER_ENCODING, CURLOPT_DNS_SERVERS and CURLOPT_USE_SSL.
|
||||
(Pierrick)
|
||||
. Fixed bug #55635 (CURLOPT_BINARYTRANSFER no longer used. The constant
|
||||
still exists for backward compatibility but is doing nothing). (Pierrick)
|
||||
. Fixed bug #54995 (Missing CURLINFO_RESPONSE_CODE support). (Pierrick)
|
||||
|
||||
- FPM:
|
||||
. Added openmetrics status format. (Cees-Jan Kiewiet)
|
||||
. Enable process renaming on macOS. (devnexen)
|
||||
. Added pm.max_spawn_rate option to configure max spawn child processes rate.
|
||||
(Paulius Sapragonas)
|
||||
. Fixed bug #65800 (Events port mechanism). (psumbera)
|
||||
- Datetime
|
||||
. Fixed bug #61642 (modify("+5 weekdays") returns Sunday).
|
||||
(Dmitri Iouchtchenko)
|
||||
|
||||
- FTP:
|
||||
. Convert resource<ftp> to object \FTP\Connection. (Sara)
|
||||
- Hash
|
||||
. Added support for PBKDF2 via hash_pbkdf2(). (Anthony Ferrara)
|
||||
|
||||
- GD:
|
||||
. Convert resource<gd font> to object \GdFont. (Sara)
|
||||
- Intl
|
||||
. The intl extension now requires ICU 4.0+.
|
||||
. Added intl.use_exceptions INI directive, which controls what happens when
|
||||
global errors are set together with intl.error_level. (Gustavo)
|
||||
. MessageFormatter::format() and related functions now accepted named
|
||||
arguments and mixed numeric/named arguments in ICU 4.8+. (Gustavo)
|
||||
. MessageFormatter::format() and related functions now don't error out when
|
||||
an insufficient argument count is provided. Instead, the placeholders will
|
||||
remain unsubstituted. (Gustavo)
|
||||
. MessageFormatter::parse() and MessageFormat::format() (and their static
|
||||
equivalents) don't throw away better than second precision in the arguments.
|
||||
(Gustavo)
|
||||
. IntlDateFormatter::__construct and datefmt_create() now accept for the
|
||||
$timezone argument time zone identifiers, IntlTimeZone objects, DateTimeZone
|
||||
objects and NULL. (Gustavo)
|
||||
. IntlDateFormatter::__construct and datefmt_create() no longer accept invalid
|
||||
timezone identifiers or empty strings. (Gustavo)
|
||||
. The default time zone used in IntlDateFormatter::__construct and
|
||||
datefmt_create() (when the corresponding argument is not passed or NULL is
|
||||
passed) is now the one given by date_default_timezone_get(), not the
|
||||
default ICU time zone. (Gustavo)
|
||||
. The time zone passed to the IntlDateFormatter is ignored if it is NULL and
|
||||
if the calendar passed is an IntlCalendar object -- in this case, the
|
||||
IntlCalendar's time zone will be used instead. Otherwise, the time zone
|
||||
specified in the $timezone argument is used instead. This does not affect
|
||||
old code, as IntlCalendar was introduced in this version. (Gustavo)
|
||||
. IntlDateFormatter::__construct and datefmt_create() now accept for the
|
||||
$calendar argument also IntlCalendar objects. (Gustavo)
|
||||
. IntlDateFormatter::getCalendar() and datefmt_get_calendar() return false
|
||||
if the IntlDateFormatter was set up with an IntlCalendar instead of the
|
||||
constants IntlDateFormatter::GREGORIAN/TRADITIONAL. IntlCalendar did not
|
||||
exist before this version. (Gustavo)
|
||||
. IntlDateFormatter::setCalendar() and datefmt_set_calendar() now also accept
|
||||
an IntlCalendar object, in which case its time zone is taken. Passing a
|
||||
constant is still allowed, and still keeps the time zone. (Gustavo)
|
||||
. IntlDateFormatter::setTimeZoneID() and datefmt_set_timezone_id() are
|
||||
deprecated. Use IntlDateFormatter::setTimeZone() or datefmt_set_timezone()
|
||||
instead. (Gustavo)
|
||||
. IntlDateFormatter::format() and datefmt_format() now also accept an
|
||||
IntlCalendar object for formatting. (Gustavo)
|
||||
. Added the classes: IntlCalendar, IntlGregorianCalendar, IntlTimeZone,
|
||||
IntlBreakIterator, IntlRuleBasedBreakIterator and
|
||||
IntlCodePointBreakIterator. (Gustavo)
|
||||
. Added the functions: intlcal_get_keyword_values_for_locale(),
|
||||
intlcal_get_now(), intlcal_get_available_locales(), intlcal_get(),
|
||||
intlcal_get_time(), intlcal_set_time(), intlcal_add(),
|
||||
intlcal_set_time_zone(), intlcal_after(), intlcal_before(), intlcal_set(),
|
||||
intlcal_roll(), intlcal_clear(), intlcal_field_difference(),
|
||||
intlcal_get_actual_maximum(), intlcal_get_actual_minimum(),
|
||||
intlcal_get_day_of_week_type(), intlcal_get_first_day_of_week(),
|
||||
intlcal_get_greatest_minimum(), intlcal_get_least_maximum(),
|
||||
intlcal_get_locale(), intlcal_get_maximum(),
|
||||
intlcal_get_minimal_days_in_first_week(), intlcal_get_minimum(),
|
||||
intlcal_get_time_zone(), intlcal_get_type(),
|
||||
intlcal_get_weekend_transition(), intlcal_in_daylight_time(),
|
||||
intlcal_is_equivalent_to(), intlcal_is_lenient(), intlcal_is_set(),
|
||||
intlcal_is_weekend(), intlcal_set_first_day_of_week(),
|
||||
intlcal_set_lenient(), intlcal_equals(),
|
||||
intlcal_get_repeated_wall_time_option(),
|
||||
intlcal_get_skipped_wall_time_option(),
|
||||
intlcal_set_repeated_wall_time_option(),
|
||||
intlcal_set_skipped_wall_time_option(), intlcal_from_date_time(),
|
||||
intlcal_to_date_time(), intlcal_get_error_code(),
|
||||
intlcal_get_error_message(), intlgregcal_create_instance(),
|
||||
intlgregcal_set_gregorian_change(), intlgregcal_get_gregorian_change() and
|
||||
intlgregcal_is_leap_year(). (Gustavo)
|
||||
. Added the functions: intltz_create_time_zone(), intltz_create_default(),
|
||||
intltz_get_id(), intltz_get_gmt(), intltz_get_unknown(),
|
||||
intltz_create_enumeration(), intltz_count_equivalent_ids(),
|
||||
intltz_create_time_zone_id_enumeration(), intltz_get_canonical_id(),
|
||||
intltz_get_region(), intltz_get_tz_data_version(),
|
||||
intltz_get_equivalent_id(), intltz_use_daylight_time(), intltz_get_offset(),
|
||||
intltz_get_raw_offset(), intltz_has_same_rules(), intltz_get_display_name(),
|
||||
intltz_get_dst_savings(), intltz_from_date_time_zone(),
|
||||
intltz_to_date_time_zone(), intltz_get_error_code(),
|
||||
intltz_get_error_message(). (Gustavo)
|
||||
. Added the methods: IntlDateFormatter::formatObject(),
|
||||
IntlDateFormatter::getCalendarObject(), IntlDateFormatter::getTimeZone(),
|
||||
IntlDateFormatter::setTimeZone(). (Gustavo)
|
||||
. Added the functions: datefmt_format_object(), datefmt_get_calendar_object(),
|
||||
datefmt_get_timezone(), datefmt_set_timezone(),
|
||||
datefmt_get_calendar_object(), intlcal_create_instance(). (Gustavo)
|
||||
|
||||
- hash:
|
||||
. Implemented FR #68109 (Add MurmurHash V3). (Anatol, Michael)
|
||||
. Implemented FR #73385 (Add xxHash support). (Anatol)
|
||||
- MCrypt
|
||||
. mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb() and mcrypt_ofb() now throw
|
||||
E_DEPRECATED. (GoogleGuy)
|
||||
|
||||
- LDAP:
|
||||
. Convert resource<ldap link> to object \LDAP\Connection. (Máté)
|
||||
. Convert resource<ldap result> to object \LDAP\Result. (Máté)
|
||||
. Convert resource<ldap result entry> to object \LDAP\ResultEntry. (Máté)
|
||||
|
||||
- MySQLi:
|
||||
. Fixed bug #70372 (Emulate mysqli_fetch_all() for libmysqlclient). (Nikita)
|
||||
. Fixed bug #80330 (Replace language in APIs and source code/docs).
|
||||
(Darek Ślusarczyk)
|
||||
. Fixed bug #80329 (Add option to specify LOAD DATA LOCAL white list folder
|
||||
(including libmysql)). (Darek Ślusarczyk)
|
||||
|
||||
- MySQLnd:
|
||||
. Fixed bug #80761 (PDO uses too much memory). (Nikita)
|
||||
|
||||
- OPcache:
|
||||
. Added inheritance cache. (Dmitry)
|
||||
|
||||
- OpenSSL:
|
||||
. Bump minimal OpenSSL version to 1.0.2. (Jakub Zelenka)
|
||||
- MySQLi
|
||||
. Dropped support for LOAD DATA LOCAL INFILE handlers when using libmysql.
|
||||
Known for stability problems. (Andrey)
|
||||
. Added support for SHA256 authentication available with MySQL 5.6.6+. (Andrey)
|
||||
|
||||
- PCRE:
|
||||
. Bundled PCRE2 is 10.37.
|
||||
. Deprecated the /e modifier
|
||||
(https://wiki.php.net/rfc/remove_preg_replace_eval_modifier). (Nikita Popov)
|
||||
. Fixed bug #63284 (Upgrade PCRE to 8.31). (Anatoliy)
|
||||
|
||||
- PDO:
|
||||
. Fixed bug #40913 (PDO_MYSQL: PDO::PARAM_LOB does not bind to a stream for
|
||||
fetching a BLOB). (Nikita)
|
||||
- pgsql
|
||||
. Added pg_escape_literal() and pg_escape_identifier() (Yasuo)
|
||||
|
||||
. PDO MySQL:
|
||||
. Fixed bug #80908 (PDO::lastInsertId() return wrong). (matt)
|
||||
. Fixed bug #81037 (PDO discards error message text from prepared
|
||||
statement). (Kamil Tekiela)
|
||||
- SPL
|
||||
. Fix bug #60560 (SplFixedArray un-/serialize, getSize(), count() return 0,
|
||||
keys are strings). (Adam)
|
||||
|
||||
. PDO ODBC:
|
||||
. Implement PDO_ATTR_SERVER_VERSION and PDO_ATTR_SERVER_INFO for
|
||||
PDO::getAttribute(). (Calvin Buckley)
|
||||
- Tokenizer:
|
||||
. Fixed bug #60097 (token_get_all fails to lex nested heredoc). (Nikita Popov)
|
||||
|
||||
. PDO SQLite:
|
||||
. Fixed bug #38334 (Proper data-type support for PDO_SQLITE). (Nikita)
|
||||
- Zip:
|
||||
. Upgraded libzip to 0.10.1 (Anatoliy)
|
||||
|
||||
- PgSQL:
|
||||
. Convert resource<pgsql link> to object \PgSql\Connection. (Máté)
|
||||
. Convert resource<pgsql result> to object \PgSql\Result. (Máté)
|
||||
. Convert resource<pgsql large object> to object \PgSql\Lob. (Máté)
|
||||
|
||||
- PSpell:
|
||||
. Convert resource<pspell> to object \PSpell\Dictionary. (Sara)
|
||||
. Convert resource<pspell config> to object \PSpell\Config. (Sara)
|
||||
|
||||
- readline:
|
||||
. Fixed bug #72998 (invalid read in readline completion). (krakjoe)
|
||||
|
||||
- Reflection:
|
||||
. Implement ReflectionFunctionAbstract::getClosureUsedVariables (krakjoe)
|
||||
|
||||
- Sodium:
|
||||
. Added the XChaCha20 stream cipher functions. (P.I.E. Security Team)
|
||||
. Added the Ristretto255 functions, which are available in libsodium 1.0.18.
|
||||
(P.I.E. Security Team)
|
||||
|
||||
- SPL:
|
||||
. Fixed bug #80724 (FilesystemIterator::FOLLOW_SYMLINKS remove KEY_AS_FILE
|
||||
from bitmask). (Cameron Porter)
|
||||
- Fileinfo:
|
||||
. Fixed bug #63248 (Load multiple magic files from a directory under Windows).
|
||||
(Anatoliy)
|
||||
|
||||
<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
|
||||
@@ -0,0 +1,39 @@
|
||||
Between PHP 4.0.6 and 4.1.0, the Zend module struct changed in a way
|
||||
that broke both source and binary compatibility. If you are
|
||||
maintaining a third party extension, here's how to update it:
|
||||
|
||||
If this was your old module entry:
|
||||
|
||||
zend_module_entry foo_module_entry = {
|
||||
"foo", /* extension name */
|
||||
foo_functions, /* extension function list */
|
||||
NULL, /* extension-wide startup function */
|
||||
NULL, /* extension-wide shutdown function */
|
||||
PHP_RINIT(foo), /* per-request startup function */
|
||||
PHP_RSHUTDOWN(foo), /* per-request shutdown function */
|
||||
PHP_MINFO(foo), /* information function */
|
||||
STANDARD_MODULE_PROPERTIES
|
||||
};
|
||||
|
||||
Here's how it should look if you want your code to build with PHP
|
||||
4.1.0 and up:
|
||||
|
||||
zend_module_entry foo_module_entry = {
|
||||
#if ZEND_MODULE_API_NO >= 20010901
|
||||
STANDARD_MODULE_HEADER,
|
||||
#endif
|
||||
"foo", /* extension name */
|
||||
foo_functions, /* extension function list */
|
||||
NULL, /* extension-wide startup function */
|
||||
NULL, /* extension-wide shutdown function */
|
||||
PHP_RINIT(foo), /* per-request startup function */
|
||||
PHP_RSHUTDOWN(foo), /* per-request shutdown function */
|
||||
PHP_MINFO(foo), /* information function */
|
||||
#if ZEND_MODULE_API_NO >= 20010901
|
||||
FOO_VERSION, /* extension version number (string) */
|
||||
#endif
|
||||
STANDARD_MODULE_PROPERTIES
|
||||
};
|
||||
|
||||
If you don't care about source compatibility with earlier PHP releases
|
||||
than 4.1.0, you can drop the #if/#endif lines.
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
(NOTE: you may also want to take a look at the pear package
|
||||
PECL_Gen, a PHP-only alternative for this script that
|
||||
supports way more extension writing tasks and is
|
||||
supposed to replace ext_skel completely in the long run ...)
|
||||
|
||||
WHAT IT IS
|
||||
|
||||
It's a tool for automatically creating the basic framework for a PHP module
|
||||
and writing C code handling arguments passed to your functions from a simple
|
||||
configuration file. See an example at the end of this file.
|
||||
|
||||
HOW TO USE IT
|
||||
|
||||
Very simple. First, change to the ext/ directory of the PHP 4 sources. If
|
||||
you just need the basic framework and will be writing all the code in your
|
||||
functions yourself, you can now do
|
||||
|
||||
./ext_skel --extname=module_name
|
||||
|
||||
and everything you need is placed in directory module_name.
|
||||
|
||||
[ Note that GNU awk is likely required for this script to work. Debian
|
||||
systems seem to default to using mawk, so you may need to change the
|
||||
#! line in skeleton/create_stubs and the cat $proto | awk line in
|
||||
ext_skel to use gawk explicitly. ]
|
||||
|
||||
If you don't need to test the existence of any external header files,
|
||||
libraries or functions in them, the module is already almost ready to be
|
||||
compiled in PHP. Just remove 3 comments in your_module_name/config.m4,
|
||||
change back up to PHP sources top directory, and do
|
||||
|
||||
./buildconf; ./configure --enable-module_name; make
|
||||
|
||||
But if you already have planned the overall scheme of your module, what
|
||||
functions it will contain, their return types and the arguments they take
|
||||
(a very good idea) and don't want to bother yourself with creating function
|
||||
definitions and handling arguments passed yourself, it's time to create a
|
||||
function definitions file, which you will give as an argument to ext_skel
|
||||
with option
|
||||
|
||||
--proto=filename.
|
||||
|
||||
FORMAT OF FUNCTION DEFINITIONS FILE
|
||||
|
||||
All the definitions must be on one line. In it's simplest form, it's just
|
||||
the function name, e.g.
|
||||
|
||||
my_function
|
||||
|
||||
but then you'll be left with an almost empty function body without any
|
||||
argument handling.
|
||||
|
||||
Arguments are given in parenthesis after the function name, and are of
|
||||
the form 'argument_type argument_name'. Arguments are separated from each
|
||||
other with a comma and optional space. Argument_type can be one of int,
|
||||
bool, double, float, string, array, object or mixed.
|
||||
|
||||
An optional argument is separated from the previous by an optional space,
|
||||
then '[' and of course comma and optional space, like all the other
|
||||
arguments. You should close a row of optional arguments with same amount of
|
||||
']'s as there where '['s. Currently, it does not harm if you forget to do it
|
||||
or there is a wrong amount of ']'s, but this may change in the future.
|
||||
|
||||
An additional short description may be added after the parameters.
|
||||
If present it will be filled into the 'proto' header comments in the stubs
|
||||
code and the <refpurpose> tag in the XML documentation.
|
||||
|
||||
An example:
|
||||
|
||||
my_function(int arg1, int arg2 [, int arg3 [, int arg4]]) this is my 1st
|
||||
|
||||
Arguments arg3 and arg4 are optional.
|
||||
|
||||
If possible, the function definition should also contain it's return type
|
||||
in front of the definition. It's not actually used for any C code generating
|
||||
purposes but PHP in-source documentation instead, and as such, very useful.
|
||||
It can be any of int, double, string, bool, array, object, resource, mixed
|
||||
or void.
|
||||
|
||||
The file must contain nothing else but function definitions, no comments or
|
||||
empty lines.
|
||||
|
||||
OTHER OPTIONS
|
||||
|
||||
--no-help
|
||||
|
||||
By default, ext_skel creates both comments in the source code and a test
|
||||
function to help first time module writers to get started and testing
|
||||
configuring and compiling their module. This option turns off all such things
|
||||
which may just annoy experienced PHP module coders. Especially useful with
|
||||
|
||||
--stubs=file
|
||||
|
||||
which will leave out also all module specific stuff and write just function
|
||||
stubs with function value declarations and passed argument handling, and
|
||||
function entries and definitions at the end of the file, for copying and
|
||||
pasting into an already existing module.
|
||||
|
||||
--xml[=file]
|
||||
|
||||
Creates the basics for phpdoc .xml file.
|
||||
|
||||
--full-xml
|
||||
|
||||
Not implemented yet. When or if there will ever be created a framework for
|
||||
self-contained extensions to use phpdoc system for their documentation, this
|
||||
option enables it on the created xml file.
|
||||
|
||||
CURRENT LIMITATIONS, BUGS AND OTHER ODDITIES
|
||||
|
||||
Only arguments of types int, bool, double, float, string and array are
|
||||
handled. For other types you must write the code yourself. And for type
|
||||
mixed, it wouldn't even be possible to write anything, because only you
|
||||
know what to expect.
|
||||
|
||||
It can't handle correctly, and probably never will, variable list of
|
||||
of arguments. (void foo(int bar [, ...])
|
||||
|
||||
Don't trust the generated code too much. It tries to be useful in most of
|
||||
the situations you might encounter, but automatic code generation will never
|
||||
beat a programmer who knows the real situation at hand. ext_skel is generally
|
||||
best suited for quickly generating a wrapper for c-library functions you
|
||||
might want to have available in PHP too.
|
||||
|
||||
This program doesn't have a --help option. It has --no-help instead.
|
||||
|
||||
EXAMPLE
|
||||
|
||||
The following _one_ line
|
||||
|
||||
bool my_drawtext(resource image, string text, resource font, int x, int y [, int color])
|
||||
|
||||
will create this function definition for you (note that there are a few
|
||||
question marks to be replaced by you, and you must of course add your own
|
||||
value definitions too):
|
||||
|
||||
/* {{{ proto bool my_drawtext(resource image, string text, resource font, int x, int y [, int color])
|
||||
*/
|
||||
PHP_FUNCTION(my_drawtext)
|
||||
{
|
||||
char *text = NULL;
|
||||
int argc = ZEND_NUM_ARGS();
|
||||
int image_id = -1;
|
||||
int text_len;
|
||||
int font_id = -1;
|
||||
long x;
|
||||
long y;
|
||||
long color;
|
||||
zval *image = NULL;
|
||||
zval *font = NULL;
|
||||
|
||||
if (zend_parse_parameters(argc TSRMLS_CC, "rsrll|l", &image, &text, &text_len, &font, &x, &y, &color) == FAILURE)
|
||||
return;
|
||||
|
||||
if (image) {
|
||||
ZEND_FETCH_RESOURCE(???, ???, image, image_id, "???", ???_rsrc_id);
|
||||
}
|
||||
if (font) {
|
||||
ZEND_FETCH_RESOURCE(???, ???, font, font_id, "???", ???_rsrc_id);
|
||||
}
|
||||
|
||||
php_error(E_WARNING, "my_drawtext: not yet implemented");
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
====================
|
||||
Git Commit Rules
|
||||
====================
|
||||
|
||||
This is the first file you should be reading when contributing code via Git.
|
||||
We'll assume you're basically familiar with Git, but feel free to post
|
||||
your questions on the mailing list. Please have a look at
|
||||
http://git-scm.com/ for more detailed information on Git.
|
||||
|
||||
PHP is developed through the efforts of a large number of people.
|
||||
Collaboration is a Good Thing(tm), and Git lets us do this. Thus, following
|
||||
some basic rules with regards to Git usage will::
|
||||
|
||||
a. Make everybody happier, especially those responsible for maintaining
|
||||
Git itself.
|
||||
|
||||
b. Keep the changes consistently well documented and easily trackable.
|
||||
|
||||
c. Prevent some of those 'Oops' moments.
|
||||
|
||||
d. Increase the general level of good will on planet Earth.
|
||||
|
||||
Having said that, here are the organizational rules::
|
||||
|
||||
1. Respect other people working on the project.
|
||||
|
||||
2. Discuss any significant changes on the list before committing and get
|
||||
confirmation from the release manager for the given branch.
|
||||
|
||||
3. Look at EXTENSIONS file to see who is the primary maintainer of
|
||||
the code you want to contribute to.
|
||||
|
||||
4. If you "strongly disagree" about something another person did, don't
|
||||
start fighting publicly - take it up in private email.
|
||||
|
||||
5. If you don't know how to do something, ask first!
|
||||
|
||||
6. Test your changes before committing them. We mean it. Really.
|
||||
To do so use "make test".
|
||||
|
||||
7. For development use the --enable-maintainer-zts switch to ensure your
|
||||
code handles TSRM correctly and doesn't break for those who need that.
|
||||
|
||||
Currently we have the following branches in use::
|
||||
|
||||
master The active development branch.
|
||||
|
||||
PHP-5.5 Is used to release the PHP 5.5.x series. It allows for larger
|
||||
enhancements.
|
||||
|
||||
PHP-5.4 Is used to release the PHP 5.4.x series. This is current
|
||||
stable version and is open for bugfixes only.
|
||||
|
||||
PHP-5.3 Is used to release the PHP 5.3.x series. This is current
|
||||
stable version and is open for bugfixes only.
|
||||
|
||||
PHP-5.2 This branch is closed.
|
||||
|
||||
PHP-5.1 This branch is closed.
|
||||
|
||||
PHP-4.4 This branch is closed.
|
||||
|
||||
The next few rules are more of a technical nature::
|
||||
|
||||
1. All changes should first go to the lowest branch (i.e. 5.3) and then
|
||||
get merged up to all other branches. If a change is not needed for
|
||||
later branches (i.e. fixes for features which where dropped from later
|
||||
branches) an empty merge should be done.
|
||||
|
||||
2. All news updates intended for public viewing, such as new features,
|
||||
bug fixes, improvements, etc., should go into the NEWS file of the
|
||||
*first* to be released version with the given change. In other words
|
||||
any NEWS file change only needs to done in one branch.
|
||||
|
||||
3. Do not commit multiple file and dump all messages in one commit. If you
|
||||
modified several unrelated files, commit each group separately and
|
||||
provide a nice commit message for each one. See example below.
|
||||
|
||||
4. Do write your commit message in such a way that it makes sense even
|
||||
without the corresponding diff. One should be able to look at it, and
|
||||
immediately know what was modified. Definitely include the function name
|
||||
in the message as shown below.
|
||||
|
||||
5. In your commit messages, keep each line shorter than 80 characters. And
|
||||
try to align your lines vertically, if they wrap. It looks bad otherwise.
|
||||
|
||||
6. If you modified a function that is callable from PHP, prepend PHP to
|
||||
the function name as shown below.
|
||||
|
||||
|
||||
The format of the commit messages is pretty simple.
|
||||
|
||||
<max 79 characters short description>\n
|
||||
\n
|
||||
<long description, 79 chars per line>
|
||||
\n
|
||||
|
||||
An Example from the git project (commit 2b34e486bc):
|
||||
|
||||
pack-objects: Fix compilation with NO_PTHREDS
|
||||
|
||||
It looks like commit 99fb6e04 (pack-objects: convert to use
|
||||
parse_options(), 2012-02-01) moved the #ifdef NO_PTHREDS around but
|
||||
hasn't noticed that the 'arg' variable no longer is available.
|
||||
|
||||
If you fix some bugs, you should note the bug ID numbers in your
|
||||
commit message. Bug ID should be prefixed by "#" for easier access to
|
||||
bug report when developers are browsing CVS via LXR or Bonsai.
|
||||
|
||||
Example::
|
||||
|
||||
Fixed bug #14016 (pgsql notice handler double free crash bug.)
|
||||
|
||||
When you change the NEWS file for a bug fix, then please keep the bugs
|
||||
sorted in decreasing order under the fixed version.
|
||||
|
||||
You can use OpenGrok (http://lxr.php.net/) and gitweb (http://git.php.net/)
|
||||
to look at PHP Git repository in various ways.
|
||||
|
||||
|
||||
For further information on the process and further details please refer to
|
||||
https://wiki.php.net/vcs/gitworkflow and https://wiki.php.net/vcs/gitfaq
|
||||
|
||||
Happy hacking,
|
||||
|
||||
PHP Team
|
||||
@@ -0,0 +1,79 @@
|
||||
====================
|
||||
Mailinglist Rules
|
||||
====================
|
||||
|
||||
This is the first file you should be reading before doing any posts on PHP
|
||||
mailinglists. Following these rules is considered imperative to the success of
|
||||
the PHP project. Therefore expect your contributions to be of much less positive
|
||||
impact if you do not follow these rules. More importantly you can actually
|
||||
assume that not following these rules will hurt the PHP project.
|
||||
|
||||
PHP is developed through the efforts of a large number of people.
|
||||
Collaboration is a Good Thing(tm), and mailinglists lets us do this. Thus,
|
||||
following some basic rules with regards to mailinglist usage will:
|
||||
|
||||
a. Make everybody happier, especially those responsible for developing PHP
|
||||
itself.
|
||||
|
||||
b. Help in making sure we all use our time more efficiently.
|
||||
|
||||
c. Prevent you from making a fool of yourself in public.
|
||||
|
||||
d. Increase the general level of good will on planet Earth.
|
||||
|
||||
|
||||
Having said that, here are the organizational rules:
|
||||
|
||||
1. Respect other people working on the project.
|
||||
|
||||
2. Do not post when you are angry. Any post can wait a few hours. Review
|
||||
your post after a good breather or a good nights sleep.
|
||||
|
||||
3. Make sure you pick the right mailinglist for your posting. Please review
|
||||
the descriptions on the mailinglist overview page
|
||||
(http://www.php.net/mailing-lists.php). When in doubt ask a friend or
|
||||
someone you trust on IRC.
|
||||
|
||||
4. Make sure you know what you are talking about. PHP is a very large project
|
||||
that strives to be very open. The flip side is that the core developers
|
||||
are faced with a lot of requests. Make sure that you have done your
|
||||
research before posting to the entire developer community.
|
||||
|
||||
5. Patches have a much greater chance of acceptance than just asking the
|
||||
PHP developers to implement a feature for you. For one it makes the
|
||||
discussion more concrete and it shows that the poster put thought and time
|
||||
into the request.
|
||||
|
||||
6. If you are posting to an existing thread, make sure that you know what
|
||||
previous posters have said. This is even more important the longer the
|
||||
thread is already.
|
||||
|
||||
7. Please configure your email client to use a real name and keep message
|
||||
signatures to a maximum of 2 lines if at all necessary.
|
||||
|
||||
The next few rules are more some general hints:
|
||||
|
||||
1. If you notice that your posting ratio is much higher than that of other
|
||||
people, double check the above rules. Try to wait a bit longer before
|
||||
sending your replies to give other people more time to digest your answers
|
||||
and more importantly give you the opportunity to make sure that you
|
||||
aggregate your current position into a single mail instead of multiple
|
||||
ones.
|
||||
|
||||
2. Consider taking a step back from a very active thread now and then. Maybe
|
||||
talking to some friends and fellow developers will help in understanding
|
||||
the other opinions better.
|
||||
|
||||
3. Do not top post. Place your answer underneath anyone you wish to quote
|
||||
and remove any previous comment that is not relevant to your post.
|
||||
|
||||
4. Do not high-jack threads, by bringing up entirely new topics. Please
|
||||
create an entirely new thread copying anything you wish to quote into the
|
||||
new thread.
|
||||
|
||||
Finally, additional hints on how to behave inside the virtual community can be
|
||||
found in RFC 1855 (http://www.faqs.org/rfcs/rfc1855.html).
|
||||
|
||||
Happy hacking,
|
||||
|
||||
PHP Team
|
||||
@@ -0,0 +1,142 @@
|
||||
$Id: README.NEW-OUTPUT-API 219039 2006-08-30 07:39:09Z mike $
|
||||
|
||||
|
||||
API adjustment to the old output control code:
|
||||
|
||||
Everything now resides beneath the php_output namespace,
|
||||
and there's an API call for every output handler op.
|
||||
|
||||
Checking output control layers status:
|
||||
// Using OG()
|
||||
php_output_get_status(TSRMLS_C);
|
||||
|
||||
Starting the default output handler:
|
||||
// php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC);
|
||||
php_output_start_default(TSRMLS_C);
|
||||
|
||||
Starting an user handler by zval:
|
||||
// php_start_ob_buffer(zhandler, chunk_size, erase TSRMLS_CC);
|
||||
php_output_start_user(zhandler, chunk_size, flags TSRMLS_CC);
|
||||
|
||||
Starting an internal handler whithout context:
|
||||
// php_ob_set_internal_handler(my_php_output_handler_func_t, buffer_size, "output handler name", erase TSRMLS_CC);
|
||||
php_output_start_internal(handler_name, handler_name_len, my_php_output_handler_func_t, chunk_size, flags TSRMLS_CC);
|
||||
|
||||
Starting an internal handler with context:
|
||||
// not possible with old API
|
||||
php_output_handler *h;
|
||||
h = php_output_handler_create_internal(handler_name, handler_name_len, my_php_output_handler_context_func_t, chunk_size, flags TSRMLS_CC);
|
||||
php_output_handler_set_context(h, my_context, my_context_dtor);
|
||||
php_output_handler_start(h TSRMLS_CC);
|
||||
|
||||
Testing whether a certain output handler has already been started:
|
||||
// php_ob_handler_used("output handler name" TSRMLS_CC);
|
||||
php_output_handler_started(handler_name, handler_name_len TSRMLS_CC);
|
||||
|
||||
Flushing one output buffer:
|
||||
// php_ob_end_buffer(1, 1 TSRMLS_CC);
|
||||
php_output_flush(TSRMLS_C);
|
||||
|
||||
Flushing all output buffers:
|
||||
// not possible with old API
|
||||
php_output_flush_all(TSRMLS_C);
|
||||
|
||||
Cleaning one output buffer:
|
||||
// php_ob_end_buffer(0, 1 TSRMLS_CC);
|
||||
php_output_clean(TSRMLS_C);
|
||||
|
||||
Cleaning all output buffers:
|
||||
// not possible with old API
|
||||
php_output_clean_all(TSRMLS_C);
|
||||
|
||||
Discarding one output buffer:
|
||||
// php_ob_end_buffer(0, 0 TSRMLS_CC);
|
||||
php_output_discard(TSRMLS_C);
|
||||
|
||||
Discarding all output buffers:
|
||||
// php_ob_end_buffers(0 TSRMLS_CC);
|
||||
php_output_discard_all(TSRMLS_C);
|
||||
|
||||
Stopping (and dropping) one output buffer:
|
||||
// php_ob_end_buffer(1, 0 TSRMLS_CC)
|
||||
php_output_end(TSRMLS_C);
|
||||
|
||||
Stopping (and dropping) all output buffers:
|
||||
// php_ob_end_buffers(1, 0 TSRMLS_CC);
|
||||
php_output_end_all(TSRMLS_C);
|
||||
|
||||
Retrieving output buffers contents:
|
||||
// php_ob_get_buffer(zstring TSRMLS_CC);
|
||||
php_output_get_contents(zstring TSRMLS_CC);
|
||||
|
||||
Retrieving output buffers length:
|
||||
// php_ob_get_length(zlength TSRMLS_CC);
|
||||
php_output_get_length(zlength TSRMLS_CC);
|
||||
|
||||
Retrieving output buffering level:
|
||||
// OG(nesting_level);
|
||||
php_output_get_level(TSRMLS_C);
|
||||
|
||||
Issue a warning because of an output handler conflict:
|
||||
// php_ob_init_conflict("to be started handler name", "to be tested if already started handler name" TSRMLS_CC);
|
||||
php_output_handler_conflict(new_handler_name, new_handler_name_len, set_handler_name, set_handler_name_len TSRMLS_CC);
|
||||
|
||||
Registering a conflict checking function, which will be checked prior starting the handler:
|
||||
// not possible with old API, unless hardcoding into output.c
|
||||
php_output_handler_conflict_register(handler_name, handler_name_len, my_php_output_handler_conflict_check_t TSRMLS_CC);
|
||||
|
||||
Registering a reverse conflict checking function, which will be checked prior starting the specified foreign handler:
|
||||
// not possible with old API
|
||||
php_output_handler_reverse_conflict_register(foreign_handler_name, foreign_handler_name_len, my_php_output_handler_conflict_check_t TSRMLS_CC);
|
||||
|
||||
Facilitating a context from within an output handler callable with ob_start():
|
||||
// not possible with old API
|
||||
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ, (void *) &custom_ctx_ptr_ptr TSRMLS_CC);
|
||||
|
||||
Disabling of the output handler by itself:
|
||||
//not possible with old API
|
||||
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL TSRMLS_CC);
|
||||
|
||||
Marking an output handler immutable by itself because of irreversibility of its operation:
|
||||
// not possible with old API
|
||||
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL TSRMLS_CC);
|
||||
|
||||
Restarting the output handler because of a CLEAN operation:
|
||||
// not possible with old API
|
||||
if (flags & PHP_OUTPUT_HANDLER_CLEAN) { ... }
|
||||
|
||||
Recognizing by the output handler itself if it gets discarded:
|
||||
// not possible with old API
|
||||
if ((flags & PHP_OUTPUT_HANDLER_CLEAN) && (flags & PHP_OUTPUT_HANDLER_FINAL)) { ... }
|
||||
|
||||
|
||||
Output handler hooks
|
||||
|
||||
The output handler can change its abilities at runtime. Eg. the gz handler can
|
||||
remove the CLEANABLE and REMOVABLE bits when the first output has passed through it;
|
||||
or handlers implemented in C to be used with ob_start() can contain a non-global
|
||||
context:
|
||||
PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ
|
||||
pass a void*** pointer as second arg to receive the address of a pointer
|
||||
pointer to the opaque field of the output handler context
|
||||
PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS
|
||||
pass a int* pointer as second arg to receive the flags set for the output handler
|
||||
PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL
|
||||
pass a int* pointer as second arg to receive the level of this output handler
|
||||
(starts with 0)
|
||||
PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE
|
||||
the second arg is ignored; marks the output handler to be neither cleanable
|
||||
nor removable
|
||||
PHP_OUTPUT_HANDLER_HOOK_DISABLE
|
||||
the second arg is ignored; marks the output handler as disabled
|
||||
|
||||
|
||||
Open questions
|
||||
|
||||
Should the userland API be adjusted and unified?
|
||||
|
||||
Many bits of the manual (and very first implementation) do not comply
|
||||
with the behaviour of the current (to be obsoleted) code, thus should
|
||||
the manual or the behaviour be adjusted?
|
||||
|
||||
END
|
||||
@@ -0,0 +1,219 @@
|
||||
New parameter parsing functions
|
||||
===============================
|
||||
|
||||
It should be easier to parse input parameters to an extension function.
|
||||
Hence, borrowing from Python's example, there are now a set of functions
|
||||
that given the string of type specifiers, can parse the input parameters
|
||||
and store the results in the user specified variables. This avoids most
|
||||
of the IS_* checks and convert_to_* conversions. The functions also
|
||||
check for the appropriate number of parameters, and try to output
|
||||
meaningful error messages.
|
||||
|
||||
|
||||
Prototypes
|
||||
----------
|
||||
/* Implemented. */
|
||||
int zend_parse_parameters(int num_args TSRMLS_DC, char *type_spec, ...);
|
||||
int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...);
|
||||
|
||||
The zend_parse_parameters() function takes the number of parameters
|
||||
passed to the extension function, the type specifier string, and the
|
||||
list of pointers to variables to store the results in. The _ex() version
|
||||
also takes 'flags' argument -- current only ZEND_PARSE_PARAMS_QUIET can
|
||||
be used as 'flags' to specify that the function should operate quietly
|
||||
and not output any error messages.
|
||||
|
||||
Both functions return SUCCESS or FAILURE depending on the result.
|
||||
|
||||
The auto-conversions are performed as necessary. Arrays, objects, and
|
||||
resources cannot be auto-converted.
|
||||
|
||||
|
||||
Type specifiers
|
||||
---------------
|
||||
The following list shows the type specifier, its meaning and the parameter
|
||||
types that need to be passed by address. All passed paramaters are set
|
||||
if the PHP parameter is non optional and untouched if optional and the
|
||||
parameter is not present. The only exception is O where the zend_class_entry*
|
||||
has to be provided on input and is used to verify the PHP parameter is an
|
||||
instance of that class.
|
||||
|
||||
a - array (zval*)
|
||||
A - array or object (zval *)
|
||||
b - boolean (zend_bool)
|
||||
C - class (zend_class_entry*)
|
||||
d - double (double)
|
||||
f - function or array containing php method call info (returned as
|
||||
zend_fcall_info and zend_fcall_info_cache)
|
||||
h - array (returned as HashTable*)
|
||||
H - array or HASH_OF(object) (returned as HashTable*)
|
||||
l - long (long)
|
||||
L - long, limits out-of-range numbers to LONG_MAX/LONG_MIN (long)
|
||||
o - object of any type (zval*)
|
||||
O - object of specific type given by class entry (zval*, zend_class_entry)
|
||||
p - valid path (string without null bytes in the middle) and its length (char*, int)
|
||||
r - resource (zval*)
|
||||
s - string (with possible null bytes) and its length (char*, int)
|
||||
z - the actual zval (zval*)
|
||||
Z - the actual zval (zval**)
|
||||
* - variable arguments list (0 or more)
|
||||
+ - variable arguments list (1 or more)
|
||||
|
||||
The following characters also have a meaning in the specifier string:
|
||||
| - indicates that the remaining parameters are optional, they
|
||||
should be initialized to default values by the extension since they
|
||||
will not be touched by the parsing function if they are not
|
||||
passed to it.
|
||||
/ - use SEPARATE_ZVAL_IF_NOT_REF() on the parameter it follows
|
||||
! - the parameter it follows can be of specified type or NULL (applies
|
||||
to all specifiers except for 'b', 'l', and 'd'). If NULL is passed, the
|
||||
results pointer is set to NULL as well.
|
||||
|
||||
|
||||
Note on 64bit compatibility
|
||||
---------------------------
|
||||
Please do not forget that int and long are two different things on 64bit
|
||||
OSes (int is 4 bytes and long is 8 bytes), so make sure you pass longs to "l"
|
||||
and ints to strings length (i.e. for "s" you need to pass char * and int),
|
||||
not the other way round!
|
||||
Remember: "l" is the only case when you need to pass long (and that's why
|
||||
it's "l", not "i" btw).
|
||||
|
||||
Both mistakes cause memory corruptions and segfaults on 64bit OSes:
|
||||
1)
|
||||
char *str;
|
||||
long str_len; /* XXX THIS IS WRONG!! Use int instead. */
|
||||
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len)
|
||||
|
||||
2)
|
||||
int num; /* XXX THIS IS WRONG!! Use long instead. */
|
||||
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &num)
|
||||
|
||||
If you're in doubt, use check_parameters.php script to the parameters
|
||||
and their types (it can be found in ./scripts/dev/ directory of PHP sources):
|
||||
|
||||
# php ./scripts/dev/check_parameters.php /path/to/your/sources/
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
/* Gets a long, a string and its length, and a zval */
|
||||
long l;
|
||||
char *s;
|
||||
int s_len;
|
||||
zval *param;
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lsz",
|
||||
&l, &s, &s_len, ¶m) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Gets an object of class specified by my_ce, and an optional double. */
|
||||
zval *obj;
|
||||
double d = 0.5;
|
||||
zend_class_entry *my_ce;
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|d",
|
||||
&obj, my_ce, &d) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Gets an object or null, and an array.
|
||||
If null is passed for object, obj will be set to NULL. */
|
||||
zval *obj;
|
||||
zval *arr;
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o!a",
|
||||
&obj, &arr) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Gets a separated array which can also be null. */
|
||||
zval *arr;
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/!",
|
||||
&arr) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get either a set of 3 longs or a string. */
|
||||
long l1, l2, l3;
|
||||
char *s;
|
||||
/*
|
||||
* The function expects a pointer to a integer in this case, not a long
|
||||
* or any other type. If you specify a type which is larger
|
||||
* than a 'int', the upper bits might not be initialized
|
||||
* properly, leading to random crashes on platforms like
|
||||
* Tru64 or Linux/Alpha.
|
||||
*/
|
||||
int length;
|
||||
|
||||
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
|
||||
"lll", &l1, &l2, &l3) == SUCCESS) {
|
||||
/* manipulate longs */
|
||||
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC,
|
||||
"s", &s, &length) == SUCCESS) {
|
||||
/* manipulate string */
|
||||
} else {
|
||||
/* output error */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Function that accepts only varargs (0 or more) */
|
||||
|
||||
int i, num_varargs;
|
||||
zval ***varargs = NULL;
|
||||
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "*", &varargs, &num_varargs) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_varargs; i++) {
|
||||
/* do something with varargs[i] */
|
||||
}
|
||||
|
||||
if (varargs) {
|
||||
efree(varargs);
|
||||
}
|
||||
|
||||
|
||||
/* Function that accepts a string, followed by varargs (1 or more) */
|
||||
|
||||
char *str;
|
||||
int str_len;
|
||||
int i, num_varargs;
|
||||
zval ***varargs = NULL;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s+", &str, &str_len, &varargs, &num_varargs) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_varargs; i++) {
|
||||
/* do something with varargs[i] */
|
||||
}
|
||||
|
||||
if (varargs) {
|
||||
efree(varargs);
|
||||
}
|
||||
|
||||
|
||||
/* Function that takes an array, followed by varargs, and ending with a long */
|
||||
long num;
|
||||
zval *array;
|
||||
int i, num_varargs;
|
||||
zval ***varargs = NULL;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a*l", &array, &varargs, &num_varargs, &num) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_varargs; i++) {
|
||||
/* do something with varargs[i] */
|
||||
}
|
||||
|
||||
if (varargs) {
|
||||
efree(varargs);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
1. strrpos() and strripos() now use the entire string as a needle. Be aware
|
||||
that the existing scripts may no longer work as you expect.
|
||||
|
||||
EX :
|
||||
<?php
|
||||
var_dump(strrpos("ABCDEF","DEF"));
|
||||
var_dump(strrpos("ABCDEF","DAF"));
|
||||
?>
|
||||
|
||||
Will give you different results. The former returns 3 while the latter
|
||||
returns false rather than the position of the last occurrence of 'D'.
|
||||
The same applies to strripos().
|
||||
|
||||
2. Illegal use of string offsets causes E_ERROR instead of E_WARNING.
|
||||
|
||||
EX :
|
||||
<?php
|
||||
$a = "foo";
|
||||
unset($a[0][1][2]);
|
||||
?>
|
||||
|
||||
Fatal error: Cannot use string offset as an array in ... on line 1
|
||||
|
||||
3. array_merge() was changed to accept only arrays. If a non-array variable is
|
||||
passed, a E_WARNING will be thrown for every such parameter. Be careful
|
||||
because your code may start emitting E_WARNING out of the blue.
|
||||
|
||||
4. Be careful when porting from ext/mysql to ext/mysqli. The following
|
||||
functions return NULL when no more data is available in the result set
|
||||
(ext/mysql's functions return FALSE).
|
||||
|
||||
- mysqli_fetch_row()
|
||||
- mysqli_fetch_array()
|
||||
- mysqli_fetch_assoc()
|
||||
|
||||
5. PATH_TRANSLATED server variable is no longer set implicitly under
|
||||
Apache2 SAPI in contrast to the situation in PHP 4, where it is set to the
|
||||
same value as the SCRIPT_FILENAME server variable when it is not populated
|
||||
by Apache. This change was made to comply with the CGI specification.
|
||||
Please refer to bug #23610 for further information.
|
||||
|
||||
6. Starting PHP 5.0.0 the T_ML_CONSTANT constant is no longer defined by the
|
||||
ext/tokenizer extension. If error_reporting is set to E_ALL notices will
|
||||
be produced. Instead of T_ML_CONSTANT for /* */ the T_COMMENT constant
|
||||
is used, thus both // and /* */ are resolved as the T_COMMENT constant.
|
||||
However the PHPDoc style comments /** */ ,which starting PHP 5 are parsed
|
||||
by PHP, are recongnized as T_DOC_COMMENT.
|
||||
|
||||
7. $_SERVER should be populated with argc and argv if variables_order
|
||||
includes "S". If you have specifically configured your system to not
|
||||
create $_SERVER, then of course it shouldn't be there. The change was to
|
||||
always make argc and argv available in the CLI version regardless of the
|
||||
variables_order setting. As in, the CLI version will now always populate
|
||||
the global $argc and $argv variables.
|
||||
|
||||
8. In some cases classes must be declared before used. It only happens only
|
||||
if some of the new features of PHP 5 are used. Otherwise the behaviour is
|
||||
the old.
|
||||
Example 1 (works with no errors):
|
||||
<?php
|
||||
$a = new a();
|
||||
class a {
|
||||
}
|
||||
?>
|
||||
|
||||
Example 2 (throws an error):
|
||||
<?php
|
||||
$a = new a();
|
||||
interface b{
|
||||
}
|
||||
class a implements b {
|
||||
}
|
||||
?>
|
||||
|
||||
Output (example 2) :
|
||||
Fatal error: Class 'a' not found in /tmp/cl.php on line 2
|
||||
|
||||
9. get_class() starting PHP 5 returns the name of the class as it was
|
||||
declared which may lead to problems in older scripts that rely on
|
||||
the previous behaviour - the class name is lowercased. Expect the
|
||||
same behaviour from get_parent_class() when applicable.
|
||||
Example :
|
||||
<?php
|
||||
class FooBar {
|
||||
}
|
||||
class ExtFooBar extends FooBar{}
|
||||
$a = new FooBar();
|
||||
var_dump(get_class($a), get_parent_class($a));
|
||||
?>
|
||||
|
||||
Output (PHP 4):
|
||||
string(6) "foobar"
|
||||
string(9) "extfoobar"
|
||||
|
||||
Output (PHP 5):
|
||||
string(6) "FooBar"
|
||||
string(9) "ExtFooBar"
|
||||
----------------------------------------------------------------------
|
||||
Example code that will break :
|
||||
//....
|
||||
function someMethod($p) {
|
||||
if (get_class($p) != 'helpingclass') {
|
||||
return FALSE;
|
||||
}
|
||||
//...
|
||||
}
|
||||
//...
|
||||
Possible solution is to search for get_class() and get_parent_class() in
|
||||
all your scripts and use strtolower().
|
||||
|
||||
10. get_class_methods() returns the names of the methods of a class as they
|
||||
declared. In PHP4 the names are all lowercased.
|
||||
Example code :
|
||||
<?php
|
||||
class Foo{
|
||||
function doFoo(){}
|
||||
function hasFoo(){}
|
||||
}
|
||||
var_dump(get_class_methods("Foo"));
|
||||
?>
|
||||
Output (PHP4):
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(5) "dofoo"
|
||||
[1]=>
|
||||
string(6) "hasfoo"
|
||||
}
|
||||
Output (PHP5):
|
||||
array(2) {
|
||||
[0]=>
|
||||
string(5) "doFoo"
|
||||
[1]=>
|
||||
string(6) "hasFoo"
|
||||
}
|
||||
|
||||
11. Assignment $this is impossible. Starting PHP 5.0.0 $this has special
|
||||
meaning in class methods and is recognized by the PHP parser. The latter
|
||||
will generate a parse error when assignment to $this is found
|
||||
Example code :
|
||||
<?php
|
||||
class Foo {
|
||||
function assignNew($obj) {
|
||||
$this = $obj;
|
||||
}
|
||||
}
|
||||
$a = new Foo();
|
||||
$b = new Foo();
|
||||
$a->assignNew($b);
|
||||
echo "I was executed\n";
|
||||
?>
|
||||
Output (PHP 4):
|
||||
I was executed
|
||||
Output (PHP 5):
|
||||
PHP Fatal error: Cannot re-assign $this in /tmp/this_ex.php on line 4
|
||||
|
||||
+153
-353
@@ -1,121 +1,23 @@
|
||||
1. libmagic (ext/fileinfo) see ext/fileinfo/libmagic/LICENSE
|
||||
2. libmbfl (ext/mbstring) see ext/mbstring/libmbfl/LICENSE
|
||||
3. pcre2lib (ext/pcre)
|
||||
4. ext/standard crypt
|
||||
5. ext/standard crypt's blowfish implementation
|
||||
6. ext/standard/rand
|
||||
7. ext/standard/scanf
|
||||
8. ext/standard/strnatcmp.c
|
||||
9. ext/standard/uuencode
|
||||
10. main/snprintf.c
|
||||
11. main/strlcat
|
||||
12. main/strlcpy
|
||||
13. libgd (ext/gd)
|
||||
14. ext/phar portions of tar implementations
|
||||
15. ext/phar/zip.c portion extracted from libzip
|
||||
16. libbcmath (ext/bcmath) see ext/bcmath/libbcmath/LICENSE
|
||||
17. ext/mbstring/ucgendat portions based on the ucgendat.c from the OpenLDAP
|
||||
1. libmagic (ext/fileinfo) see ext/fileinfo/libmagic/LICENSE
|
||||
2. Oniguruma (ext/mbstring) see ext/mbstring/oniguruma/COPYING
|
||||
3. libmbfl (ext/mbstring) see ext/mbstring/libmbfl/LICENSE
|
||||
4. pcrelib (ext/pcre) see ext/pcre/pcrelib/LICENCE
|
||||
5. ext/standard crypt
|
||||
6. ext/standard crypt's blowfish implementation
|
||||
7. Sqlite/Sqlite3 ext/sqlite3 ext/sqlite
|
||||
8. ext/json/json_parser
|
||||
9. ext/standard/rand
|
||||
10. ext/standard/scanf
|
||||
11. ext/standard/strnatcmp.c
|
||||
12. ext/standard/uuencode
|
||||
13. libxmlrpc ext/xmlrpc
|
||||
14. libzip ext/zip
|
||||
15. main/snprintf.c
|
||||
16. main/strlcat
|
||||
17. main/strlcpy
|
||||
18. libgd see ext/gd/libgd/COPYING
|
||||
|
||||
|
||||
3. pcre2lib (ext/pcre)
|
||||
|
||||
PCRE2 LICENCE
|
||||
-------------
|
||||
|
||||
PCRE2 is a library of functions to support regular expressions whose syntax
|
||||
and semantics are as close as possible to those of the Perl 5 language.
|
||||
|
||||
Releases 10.00 and above of PCRE2 are distributed under the terms of the "BSD"
|
||||
licence, as specified below, with one exemption for certain binary
|
||||
redistributions. The documentation for PCRE2, supplied in the "doc" directory,
|
||||
is distributed under the same terms as the software itself. The data in the
|
||||
testdata directory is not copyrighted and is in the public domain.
|
||||
|
||||
The basic library functions are written in C and are freestanding. Also
|
||||
included in the distribution is a just-in-time compiler that can be used to
|
||||
optimize pattern matching. This is an optional feature that can be omitted when
|
||||
the library is built.
|
||||
|
||||
|
||||
THE BASIC LIBRARY FUNCTIONS
|
||||
---------------------------
|
||||
|
||||
Written by: Philip Hazel
|
||||
Email local part: ph10
|
||||
Email domain: cam.ac.uk
|
||||
|
||||
University of Cambridge Computing Service,
|
||||
Cambridge, England.
|
||||
|
||||
Copyright (c) 1997-2019 University of Cambridge
|
||||
All rights reserved.
|
||||
|
||||
|
||||
PCRE2 JUST-IN-TIME COMPILATION SUPPORT
|
||||
--------------------------------------
|
||||
|
||||
Written by: Zoltan Herczeg
|
||||
Email local part: hzmester
|
||||
Email domain: freemail.hu
|
||||
|
||||
Copyright(c) 2010-2019 Zoltan Herczeg
|
||||
All rights reserved.
|
||||
|
||||
|
||||
STACK-LESS JUST-IN-TIME COMPILER
|
||||
--------------------------------
|
||||
|
||||
Written by: Zoltan Herczeg
|
||||
Email local part: hzmester
|
||||
Email domain: freemail.hu
|
||||
|
||||
Copyright(c) 2009-2019 Zoltan Herczeg
|
||||
All rights reserved.
|
||||
|
||||
|
||||
THE "BSD" LICENCE
|
||||
-----------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notices,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notices, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Cambridge nor the names of any
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
EXEMPTION FOR BINARY LIBRARY-LIKE PACKAGES
|
||||
------------------------------------------
|
||||
|
||||
The second condition in the BSD licence (covering binary redistributions) does
|
||||
not apply all the way down a chain of software. If binary package A includes
|
||||
PCRE2, it must respect the condition, but if package B is software that
|
||||
includes package A, the condition is not imposed on package B unless it uses
|
||||
PCRE2 independently.
|
||||
|
||||
End
|
||||
|
||||
|
||||
4. ext/standard crypt
|
||||
5. ext/standard crypt
|
||||
|
||||
FreeSec: libcrypt for NetBSD
|
||||
|
||||
@@ -126,13 +28,13 @@ Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the author nor the names of other contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
@@ -145,9 +47,9 @@ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
|
||||
|
||||
5. ext/standard crypt's blowfish implementation
|
||||
6. ext/standard crypt's blowfish implementation
|
||||
|
||||
The crypt_blowfish homepage is:
|
||||
|
||||
@@ -191,10 +93,42 @@ http://www.usenix.org/events/usenix99/provos.html
|
||||
Some of the tricks in BF_ROUND might be inspired by Eric Young's
|
||||
Blowfish library (I can't be sure if I would think of something if I
|
||||
hadn't seen his code).
|
||||
|
||||
|
||||
7. Sqlite/Sqlite3 ext/sqlite3 ext/sqlite
|
||||
|
||||
The author disclaims copyright to this source code. In place of
|
||||
a legal notice, here is a blessing:
|
||||
May you do good and not evil.
|
||||
May you find forgiveness for yourself and forgive others.
|
||||
May you share freely, never taking more than you give.
|
||||
|
||||
|
||||
6. ext/standard/rand
|
||||
8. ext/json/json_parser
|
||||
Copyright (c) 2005 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
9. ext/standard/rand
|
||||
The following php_mt_...() functions are based on a C++ class MTRand by
|
||||
Richard J. Wagner. For more information see the web page at
|
||||
http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html
|
||||
@@ -217,22 +151,22 @@ Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
|
||||
|
||||
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||
Copyright (C) 2000 - 2003, Richard J. Wagner
|
||||
All rights reserved.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of its contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
3. The names of its contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
@@ -247,8 +181,7 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
7. ext/standard/scanf
|
||||
|
||||
10. ext/standard/scanf
|
||||
scanf.c --
|
||||
|
||||
This file contains the base code which implements sscanf and by extension
|
||||
@@ -294,8 +227,7 @@ authors grant the U.S. Government and others acting in its behalf
|
||||
permission to use and distribute the software in accordance with the
|
||||
terms specified in this license.
|
||||
|
||||
|
||||
8. ext/standard/strnatcmp.c
|
||||
11. ext/standard/strnatcmp.c
|
||||
|
||||
strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
|
||||
Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
|
||||
@@ -309,16 +241,14 @@ 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.
|
||||
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.
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
9. ext/standard/uuencode
|
||||
|
||||
12. ext/standard/uuencode
|
||||
Portions of this code are based on Berkeley's uuencode/uudecode
|
||||
implementation.
|
||||
|
||||
@@ -329,17 +259,17 @@ Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
must display the following acknowledgement:
|
||||
This product includes software developed by the University of
|
||||
California, Berkeley and its contributors.
|
||||
4. Neither the name of the University nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
@@ -354,8 +284,63 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
|
||||
10. main/snprintf.c
|
||||
13. libxmlrpc ext/xmlrpc
|
||||
|
||||
Copyright 2000 Epinions, Inc.
|
||||
|
||||
Subject to the following 3 conditions, Epinions, Inc. permits you, free
|
||||
of charge, to (a) use, copy, distribute, modify, perform and display this
|
||||
software and associated documentation files (the "Software"), and (b)
|
||||
permit others to whom the Software is furnished to do so as well.
|
||||
|
||||
1) The above copyright notice and this permission notice shall be included
|
||||
without modification in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF
|
||||
ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY
|
||||
IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE OR NONINFRINGEMENT.
|
||||
|
||||
3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING
|
||||
NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
14. libzip ext/zip
|
||||
zip.h -- exported declarations.
|
||||
Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
|
||||
|
||||
This file is part of libzip, a library to manipulate ZIP archives.
|
||||
The authors can be contacted at <libzip@nih.at>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
3. The names of the authors may not be used to endorse or promote
|
||||
products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
15. main/snprintf.c
|
||||
Copyright (c) 2002, 2006 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
@@ -382,26 +367,26 @@ modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. All advertising materials mentioning features or use of this
|
||||
software must display the following acknowledgment:
|
||||
"This product includes software developed by the Apache Group
|
||||
for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
software must display the following acknowledgment:
|
||||
"This product includes software developed by the Apache Group
|
||||
for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
|
||||
4. The names "Apache Server" and "Apache Group" must not be used to
|
||||
endorse or promote products derived from this software without
|
||||
prior written permission.
|
||||
endorse or promote products derived from this software without
|
||||
prior written permission.
|
||||
|
||||
5. Redistributions of any form whatsoever must retain the following
|
||||
acknowledgment:
|
||||
"This product includes software developed by the Apache Group
|
||||
for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
acknowledgment:
|
||||
"This product includes software developed by the Apache Group
|
||||
for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
|
||||
EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
@@ -428,10 +413,8 @@ This code is based on, and used with the permission of, the
|
||||
SIO stdio-replacement strx_* functions by Panos Tsirigotis
|
||||
<panos@alumni.cs.colorado.edu> for xinetd.
|
||||
|
||||
|
||||
11. main/strlcat
|
||||
12. main/strlcpy
|
||||
|
||||
16. main/strlcat
|
||||
17. main/strlcpy
|
||||
Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
All rights reserved.
|
||||
|
||||
@@ -439,12 +422,12 @@ Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
@@ -457,186 +440,3 @@ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
13. libgd (ext/gd)
|
||||
|
||||
* Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
|
||||
2002, 2003, 2004 by Cold Spring Harbor Laboratory. Funded under
|
||||
Grant P41-RR02188 by the National Institutes of Health.
|
||||
|
||||
* Portions copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
|
||||
2004 by Boutell.Com, Inc.
|
||||
|
||||
* Portions relating to GD2 format copyright 1999, 2000, 2001, 2002,
|
||||
2003, 2004 Philip Warner.
|
||||
|
||||
* Portions relating to PNG copyright 1999, 2000, 2001, 2002, 2003,
|
||||
2004 Greg Roelofs.
|
||||
|
||||
* Portions relating to gdttf.c copyright 1999, 2000, 2001, 2002,
|
||||
2003, 2004 John Ellson (ellson@graphviz.org).
|
||||
|
||||
* Portions relating to gdft.c copyright 2001, 2002, 2003, 2004 John
|
||||
Ellson (ellson@graphviz.org).
|
||||
|
||||
* Portions copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
|
||||
Pierre-Alain Joye (pierre@libgd.org).
|
||||
|
||||
* Portions relating to JPEG and to color quantization copyright
|
||||
2000, 2001, 2002, 2003, 2004, Doug Becker and copyright (C) 1994,
|
||||
1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Thomas
|
||||
G. Lane. This software is based in part on the work of the
|
||||
Independent JPEG Group. See the file README-JPEG.TXT for more
|
||||
information.
|
||||
|
||||
* Portions relating to GIF compression copyright 1989 by Jef
|
||||
Poskanzer and David Rowley, with modifications for thread safety
|
||||
by Thomas Boutell.
|
||||
|
||||
* Portions relating to GIF decompression copyright 1990, 1991, 1993
|
||||
by David Koblas, with modifications for thread safety by Thomas
|
||||
Boutell.
|
||||
|
||||
* Portions relating to WBMP copyright 2000, 2001, 2002, 2003, 2004
|
||||
Maurice Szmurlo and Johan Van den Brande.
|
||||
|
||||
* Portions relating to GIF animations copyright 2004 Jaakko Hyvätti
|
||||
(jaakko.hyvatti@iki.fi)
|
||||
|
||||
Permission has been granted to copy, distribute and modify gd in
|
||||
any context without fee, including a commercial application,
|
||||
provided that this notice is present in user-accessible supporting
|
||||
documentation.
|
||||
|
||||
This does not affect your ownership of the derived work itself,
|
||||
and the intent is to assure proper credit for the authors of gd,
|
||||
not to interfere with your productive use of gd. If you have
|
||||
questions, ask. "Derived works" includes all programs that utilize
|
||||
the library. Credit must be given in user-accessible
|
||||
documentation.
|
||||
|
||||
This software is provided "AS IS." The copyright holders disclaim
|
||||
all warranties, either express or implied, including but not
|
||||
limited to implied warranties of merchantability and fitness for a
|
||||
particular purpose, with respect to this code and accompanying
|
||||
documentation.
|
||||
|
||||
Although their code does not appear in the current release, the
|
||||
authors wish to thank David Koblas, David Rowley, and Hutchison
|
||||
Avenue Software Corporation for their prior contributions.
|
||||
|
||||
END OF COPYRIGHT STATEMENT
|
||||
|
||||
|
||||
14. ext/phar portions of tar implementations
|
||||
|
||||
portions of tar implementations in ext/phar - phar_tar_octal() are based on an
|
||||
implementation by Tim Kientzle from libarchive, licensed with this license:
|
||||
|
||||
Copyright (c) 2003-2007 Tim Kientzle
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
15. ext/phar/zip.c portion extracted from libzip
|
||||
|
||||
zip_dirent.c -- read directory entry (local or central), clean dirent
|
||||
Copyright (C) 1999, 2003, 2004, 2005 Dieter Baron and Thomas Klausner
|
||||
|
||||
This function is part of libzip, a library to manipulate ZIP archives.
|
||||
The authors can be contacted at <nih@giga.or.at>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
3. The names of the authors may not be used to endorse or promote
|
||||
products derived from this software without specific prior
|
||||
written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
|
||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
16. ext/mbstring/ucgendat portions based on the ucgendat.c from the OpenLDAP
|
||||
|
||||
The OpenLDAP Public License
|
||||
Version 2.8, 17 August 2003
|
||||
|
||||
Redistribution and use of this software and associated documentation
|
||||
("Software"), with or without modification, are permitted provided
|
||||
that the following conditions are met:
|
||||
|
||||
1. Redistributions in source form must retain copyright statements
|
||||
and notices,
|
||||
|
||||
2. Redistributions in binary form must reproduce applicable copyright
|
||||
statements and notices, this list of conditions, and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution, and
|
||||
|
||||
3. Redistributions must contain a verbatim copy of this document.
|
||||
|
||||
The OpenLDAP Foundation may revise this license from time to time.
|
||||
Each revision is distinguished by a version number. You may use
|
||||
this Software under terms of this license revision or under the
|
||||
terms of any subsequent revision of the license.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
|
||||
CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
|
||||
OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The names of the authors and copyright holders must not be used in
|
||||
advertising or otherwise to promote the sale, use or other dealing
|
||||
in this Software without specific, written prior permission. Title
|
||||
to copyright in this Software shall at all times remain with copyright
|
||||
holders.
|
||||
|
||||
OpenLDAP is a registered trademark of the OpenLDAP Foundation.
|
||||
|
||||
Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
|
||||
California, USA. All Rights Reserved. Permission to copy and
|
||||
distribute verbatim copies of this document is granted.
|
||||
@@ -0,0 +1,270 @@
|
||||
=======================
|
||||
PHP Release Process
|
||||
=======================
|
||||
|
||||
General notes and tips
|
||||
----------------------
|
||||
|
||||
1. Do not release on Fridays, Saturdays or Sundays
|
||||
because the sysadmins can not upgrade stuff then.
|
||||
|
||||
2. Package the day before a release. So if the release is to be on Thursday,
|
||||
package on Wednesday.
|
||||
|
||||
3. Ensure that Windows builds will work before packaging
|
||||
|
||||
4. Follow all steps to the letter. When unclear ask previous RM's (Derick/Ilia)
|
||||
before proceeding. Ideally make sure that for the first releases one of the
|
||||
previous RM's is around to answer questions. For the steps related to the
|
||||
php/QA/bug websites try to have someone from the webmaster team (Bjori) on hand.
|
||||
|
||||
5. Verify the tags to be extra sure everything was tagged properly.
|
||||
|
||||
6. Moving extensions from/to PECL requires write acces to the destination.
|
||||
Most developers should have this.
|
||||
|
||||
Moving extensions from php-src to PECL
|
||||
- Checkout the pecl directory, most likely you want a sparse-root checkout
|
||||
svn co --depth=empty https://svn.php.net/repository/pecl
|
||||
- Create an directory for the extension incl. branch and tag structure,
|
||||
no trunk at this point and commit this to svn
|
||||
cd pecl; mkdir foo foo/tags foo/branches; svn add foo; svn commit
|
||||
- Move the extension from php-src to the new location
|
||||
svn mv https://svn.php.net/repository/php/php-src/trunk/ext/foo \
|
||||
https://svn.php.net/repository/pecl/foo/trunk
|
||||
|
||||
If the extension is still usable or not dead, in cooperation with the extension
|
||||
maintainers if any:
|
||||
- create the pecl.php.net/foo package and its content, license, maintainer
|
||||
- create the package.xml, commit
|
||||
- release the package
|
||||
|
||||
For Moving extensions from PECL to php-src the svn mv has to be tone the other
|
||||
way round.
|
||||
|
||||
Rolling a non stable release (alpha/beta/RC)
|
||||
--------------------------------------------
|
||||
|
||||
1. Check windows snapshot builder logs (http://windows.php.net/downloads/snaps/ the last revision)
|
||||
|
||||
2. run the "scripts/dev/credits" script in php-src and commit the changes in the
|
||||
credits files in ext/standard.
|
||||
|
||||
3. Checkout the release branch for this release (e.g., PHP-5.4.2).
|
||||
|
||||
4. Bump the version numbers in ``main/php_version.h``, ``configure.in`` and possibly ``NEWS``.
|
||||
Do not use abbreviations for alpha and beta.
|
||||
|
||||
5. Commit these changes to the branch with ``git commit -a``.
|
||||
|
||||
6. Tag the repository with the version, e.g.:
|
||||
``git tag -u YOURKEYID php-5.4.2RC2``
|
||||
|
||||
7. Push the changes to the main repo:
|
||||
``git push --tags origin HEAD``
|
||||
|
||||
8. run: ``./makedist 5.4.2RC2``, this will export the tree, create configure
|
||||
and build two tarballs (one gz and one bz2).
|
||||
|
||||
9. Copy those two tarballs to www.php.net, in your homedir there should be a
|
||||
directory "downloads/". Copy them into there, so that the system can generate
|
||||
MD5 sums. If you do not have this directory, talk to Derick.
|
||||
|
||||
10. Now the RC can be found on http://downloads.php.net/yourname,
|
||||
f.e. http://downloads.php.net/derick/
|
||||
|
||||
11. 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
|
||||
those are made, they should be placed into the same directory as the source snapshots.
|
||||
|
||||
Getting the non stable release (alpha/beta/RC) announced
|
||||
--------------------------------------------------------
|
||||
|
||||
1. Send an email (see example here: http://news.php.net/php.internals/19486)
|
||||
**To** ``internals@lists.php.net`` and ``php-general@lists.php.net`` lists
|
||||
pointing out "the location of the release" and "the possible release date of
|
||||
either the next RC, or the final release".
|
||||
|
||||
2. Send an email (see example here http://news.php.net/php.pear.qa/5201) **To**
|
||||
``php-qa@lists.php.net`` and ``primary-qa-tests@lists.php.net``.
|
||||
This email is to notify the selected projects about a new release so that they
|
||||
can make sure their projects keep working. Make sure that you have been setup
|
||||
as a moderator for ``primary-qa-tests@lists.php.net`` by having someone (Wez,
|
||||
Derick) run the following commands for you:
|
||||
|
||||
``ssh lists.php.net``
|
||||
|
||||
``sudo -u ezmlm ezmlm-sub ~ezmlm/primary-qa-tester/mod moderator-email-address``
|
||||
|
||||
3. Update ``qa.git/include/release-qa.php`` with the appropriate information.
|
||||
See the documentation within release-qa.php for more information, but all releases
|
||||
and RCs are configured here. Only $QA_RELEASES needs to be edited.
|
||||
|
||||
Example: When rolling an RC, set the 'rc' with appropriate information for the
|
||||
given version.
|
||||
|
||||
Note: Remember to update the MD5 checksum information.
|
||||
|
||||
4. Update ``php.git/include/version.inc`` (x=major version number)
|
||||
|
||||
a. ``$PHP_x_RC`` = "5.4.0RC1"
|
||||
|
||||
b. ``$PHP_x_RC_DATE`` = "06 September 2007"
|
||||
|
||||
5. Commit and push those changes:
|
||||
|
||||
a. ``git commit -a && git push origin master``
|
||||
|
||||
6. For the first RC, write the doc team (phpdoc@lists.php.net) about updating the
|
||||
INSTALL and win32/install.txt files which are generated from the PHP manual sources.
|
||||
|
||||
7. Publish the announce on www.php.net as well (for all releases, alpha, RCs or other)
|
||||
|
||||
Rolling a stable release
|
||||
------------------------
|
||||
|
||||
1. Check windows snapshot builder logs (http://snaps.php.net/win32/snapshot-STABLE.log f.e.)
|
||||
|
||||
2. Bump the version numbers in ``main/php_version.h``, ``configure.in`` and possibly ``NEWS``.
|
||||
|
||||
3. **Merge** all related sections in NEWS (f.e. merge the 5.4.1RC1 and 5.4.0 sections)
|
||||
|
||||
4. Commit those changes
|
||||
|
||||
5. run the "scripts/dev/credits" script in php-src and commit the changes in the
|
||||
credits files in ext/standard.
|
||||
|
||||
6. tag the repository with the version f.e. "``git tag -s php-5.4.1``"
|
||||
(of course, you need to change that to the version you're rolling an RC for).
|
||||
When making 5.X release, you need to tag the Zend directory separately!!
|
||||
|
||||
7. Bump up the version numbers in ``main/php_version.h``, ``configure.in`` and
|
||||
possibly ``NEWS`` again, to the **next** version. F.e. if the release candidate
|
||||
was "5.4.1RC1" then the new one should be "5.4.1RC2-dev" - regardless if we get
|
||||
a new RC or not. This is to make sure ``version_compare()`` can correctly work.
|
||||
|
||||
8. Commit those changes
|
||||
|
||||
9. Log in onto the snaps box and go into the correct tree (f.e. the PHP-5.4
|
||||
branch if you're rolling 5.5.x releases).
|
||||
|
||||
10. You do not have to update the tree, but of course you can with "``git pull
|
||||
origin <branch>``".
|
||||
|
||||
11. run: ``./makedist php 5.4.1``, this will export the tree, create configure
|
||||
and build two tarballs (one gz and one bz2).
|
||||
|
||||
12. Commit those two tarballs to Git (php-distributions.git)
|
||||
|
||||
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
|
||||
those are made, they should be committed to SVN too.
|
||||
|
||||
14. Check if the pear files are updated (phar for 5.1+ or run pear/make-pear-bundle.php with 4.4)
|
||||
|
||||
15. When making a final release, also remind the PHP Windows development team
|
||||
(internals-win@lists.php.net) to prepare the installer packages for Win32.
|
||||
|
||||
Getting the stable release announced
|
||||
------------------------------------
|
||||
|
||||
1. Run the bumpRelease script for phpweb on your local checkout
|
||||
|
||||
a. ``php bin/bumpRelease 5`` (or ``php bin/bumpRelease 4`` for PHP4)
|
||||
|
||||
b. In case multiple PHP minor versions are in active development you have
|
||||
to manually copy the old information to include/releases.inc
|
||||
|
||||
2. Edit ``phpweb/include/version.inc`` and change (X=major release number):
|
||||
|
||||
a. ``$PHP_X_VERSION`` to the correct version
|
||||
|
||||
b. ``$PHP_X_DATE`` to the release date
|
||||
|
||||
c. ``$PHP_X_MD5`` array and update all the md5 sums
|
||||
|
||||
d. set ``$PHP_X_RC`` to false!
|
||||
|
||||
e. Make sure there are no outdated "notes" or edited "date" keys in the
|
||||
``$RELEASES[X][$PHP_X_VERSION]["source"]`` array
|
||||
|
||||
f. if the windows builds aren't ready yet prefix the "windows" key with a dot (".windows")
|
||||
|
||||
3. Update the ChangeLog file for the given major version
|
||||
f.e. ``ChangeLog-5.php`` from the NEWS file
|
||||
|
||||
a. go over the list and put every element on one line
|
||||
|
||||
b. check for &, < and > and escape them if necessary
|
||||
|
||||
c. remove all the names at the ends of lines
|
||||
|
||||
d. for marking up, you can do the following (with VI):
|
||||
|
||||
I. ``s/^- /<li>/``
|
||||
|
||||
II. ``s/$/<\/li>/``
|
||||
|
||||
III. ``s/Fixed bug #\([0-9]\+\)/<?php bugfix(\1); ?>/``
|
||||
|
||||
IV. ``s/Fixed PECL bug #\([0-9]\+\)/<?php peclbugfix(\1); ?>/``
|
||||
|
||||
V. ``s/FR #\([0-9]\+\)/FR <?php bugl(\1); ?>/``
|
||||
|
||||
4. ``cp releases/5_4_0.php releases/5_4_1.php``
|
||||
|
||||
5. ``git add releases/5_4_1.php``
|
||||
|
||||
6. Update the ``releases/*.php`` file with relevant data. The release
|
||||
announcement file should list in detail:
|
||||
|
||||
a. security fixes,
|
||||
|
||||
b. changes in behavior (whether due to a bug fix or not)
|
||||
|
||||
7. Add a short notice to phpweb stating that there is a new release, and
|
||||
highlight the major important things (security fixes) and when it is important
|
||||
to upgrade.
|
||||
|
||||
a. Call php bin/createNewsEntry in your local phpweb checkout
|
||||
|
||||
b. Add the content for the news entry
|
||||
|
||||
8. Commit all the changes.
|
||||
|
||||
9. Wait an hour or two, then send a mail to php-announce@lists.php.net,
|
||||
php-general@lists.php.net and internals@lists.php.net with a text similar to
|
||||
http://news.php.net/php.internals/17222.
|
||||
|
||||
Re-releasing the same version (or -pl)
|
||||
--------------------------------------
|
||||
|
||||
1. Commit the new binaries to ``phpweb/distributions/``
|
||||
|
||||
2. Edit ``phpweb/include/version.inc`` and change (X=major release number):
|
||||
|
||||
a. If only releasing for one OS, make sure you edit only those variables
|
||||
|
||||
b. ``$PHP_X_VERSION`` to the correct version
|
||||
|
||||
c. ``$PHP_X_DATE`` to the release date
|
||||
|
||||
d. ``$PHP_X_MD5`` array and update all the md5 sums
|
||||
|
||||
e. Make sure there are no outdated "notes" or edited "date" keys in the
|
||||
``$RELEASES[X][$PHP_X_VERSION]["source"]`` array
|
||||
|
||||
3. Add a short notice to phpweb stating that there is a new release, and
|
||||
highlight the major important things (security fixes) and when it is important
|
||||
to upgrade.
|
||||
|
||||
a. Call php bin/createNewsEntry in your local phpweb checkout
|
||||
|
||||
b. Add the content for the news entry
|
||||
|
||||
4. Commit all the changes (``include/version.inc``, ``archive/archive.xml``,
|
||||
``archive/entries/YYYY-MM-DD-N.xml``)
|
||||
|
||||
5. Wait an hour or two, then send a mail to php-announce@lists.php.net,
|
||||
php-general@lists.php.net and internals@lists.php.net with a text similar to
|
||||
the news entry.
|
||||
@@ -0,0 +1,155 @@
|
||||
$Id$
|
||||
=============================================================================
|
||||
|
||||
HOW TO CREATE A SELF-CONTAINED PHP EXTENSION
|
||||
|
||||
A self-contained extension can be distributed independently of
|
||||
the PHP source. To create such an extension, two things are
|
||||
required:
|
||||
|
||||
- Configuration file (config.m4)
|
||||
- Source code for your module
|
||||
|
||||
We will describe now how to create these and how to put things
|
||||
together.
|
||||
|
||||
PREPARING YOUR SYSTEM
|
||||
|
||||
While the result will run on any system, a developer's setup needs these
|
||||
tools:
|
||||
|
||||
GNU autoconf
|
||||
GNU automake
|
||||
GNU libtool
|
||||
GNU m4
|
||||
|
||||
All of these are available from
|
||||
|
||||
ftp://ftp.gnu.org/pub/gnu/
|
||||
|
||||
CONVERTING AN EXISTING EXTENSION
|
||||
|
||||
Just to show you how easy it is to create a self-contained
|
||||
extension, we will convert an embedded extension into a
|
||||
self-contained one. Install PHP and execute the following
|
||||
commands.
|
||||
|
||||
$ mkdir /tmp/newext
|
||||
$ cd /tmp/newext
|
||||
|
||||
You now have an empty directory. We will copy the files from
|
||||
the mysql extension:
|
||||
|
||||
$ cp -rp php-4.0.X/ext/mysql/* .
|
||||
|
||||
It is time to finish the module. Run:
|
||||
|
||||
$ phpize
|
||||
|
||||
You can now ship the contents of the directory - the extension
|
||||
can live completely on its own.
|
||||
|
||||
The user instructions boil down to
|
||||
|
||||
$ ./configure \
|
||||
[--with-php-config=/path/to/php-config] \
|
||||
[--with-mysql=MYSQL-DIR]
|
||||
$ make install
|
||||
|
||||
The MySQL module will either use the embedded MySQL client
|
||||
library or the MySQL installation in MYSQL-DIR.
|
||||
|
||||
|
||||
DEFINING THE NEW EXTENSION
|
||||
|
||||
Our demo extension is called "foobar".
|
||||
|
||||
It consists of two source files "foo.c" and "bar.c"
|
||||
(and any arbitrary amount of header files, but that is not
|
||||
important here).
|
||||
|
||||
The demo extension does not reference any external
|
||||
libraries (that is important, because the user does not
|
||||
need to specify anything).
|
||||
|
||||
|
||||
LTLIBRARY_SOURCES specifies the names of the sources files. You can
|
||||
name an arbitrary number of source files here.
|
||||
|
||||
CREATING THE M4 CONFIGURATION FILE
|
||||
|
||||
The m4 configuration can perform additional checks. For a
|
||||
self-contained extension, you do not need more than a few
|
||||
macro calls.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
PHP_ARG_ENABLE(foobar,whether to enable foobar,
|
||||
[ --enable-foobar Enable foobar])
|
||||
|
||||
if test "$PHP_FOOBAR" != "no"; then
|
||||
PHP_NEW_EXTENSION(foobar, foo.c bar.c, $ext_shared)
|
||||
fi
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
PHP_ARG_ENABLE will automatically set the correct variables, so
|
||||
that the extension will be enabled by PHP_NEW_EXTENSION in shared mode.
|
||||
|
||||
The first argument of PHP_NEW_EXTENSION describes the name of the
|
||||
extension. The second names the source-code files. The third passes
|
||||
$ext_shared which is set by PHP_ARG_ENABLE/WITH to PHP_NEW_EXTENSION.
|
||||
|
||||
Please use always PHP_ARG_ENABLE or PHP_ARG_WITH. Even if you do not
|
||||
plan to distribute your module with PHP, these facilities allow you
|
||||
to integrate your module easily into the main PHP module framework.
|
||||
|
||||
CREATING SOURCE FILES
|
||||
|
||||
ext_skel can be of great help when creating the common code for all modules
|
||||
in PHP for you and also writing basic function definitions and C code for
|
||||
handling arguments passed to your functions. See README.EXT_SKEL for further
|
||||
information.
|
||||
|
||||
As for the rest, you are currently alone here. There are a lot of existing
|
||||
modules, use a simple module as a starting point and add your own code.
|
||||
|
||||
|
||||
CREATING THE SELF-CONTAINED EXTENSION
|
||||
|
||||
Put config.m4 and the source files into one directory. Then, run phpize
|
||||
(this is installed during make install by PHP 4.0).
|
||||
|
||||
For example, if you configured PHP with --prefix=/php, you would run
|
||||
|
||||
$ /php/bin/phpize
|
||||
|
||||
This will automatically copy the necessary build files and create
|
||||
configure from your config.m4.
|
||||
|
||||
And that's it. You now have a self-contained extension.
|
||||
|
||||
INSTALLING A SELF-CONTAINED EXTENSION
|
||||
|
||||
An extension can be installed by running:
|
||||
|
||||
$ ./configure \
|
||||
[--with-php-config=/path/to/php-config]
|
||||
$ make install
|
||||
|
||||
ADDING SHARED MODULE SUPPORT TO A MODULE
|
||||
|
||||
In order to be useful, a self-contained extension must be loadable
|
||||
as a shared module. I will explain now how you can add shared module
|
||||
support to an existing module called foo.
|
||||
|
||||
1. In config.m4, use PHP_ARG_WITH/PHP_ARG_ENABLE. Then you will
|
||||
automatically be able to use --with-foo=shared[,..] or
|
||||
--enable-foo=shared[,..].
|
||||
|
||||
2. In config.m4, use PHP_NEW_EXTENSION(foo,.., $ext_shared) to enable
|
||||
building the extension.
|
||||
|
||||
3. Add the following lines to your C source file:
|
||||
|
||||
#ifdef COMPILE_DL_FOO
|
||||
ZEND_GET_MODULE(foo)
|
||||
#endif
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
An Overview of the PHP Streams abstraction
|
||||
==========================================
|
||||
$Id$
|
||||
|
||||
WARNING: some prototypes in this file are out of date.
|
||||
The information contained here is being integrated into
|
||||
the PHP manual - stay tuned...
|
||||
|
||||
Please send comments to: Wez Furlong <wez@thebrainroom.com>
|
||||
|
||||
Why Streams?
|
||||
============
|
||||
You may have noticed a shed-load of issock parameters flying around the PHP
|
||||
code; we don't want them - they are ugly and cumbersome and force you to
|
||||
special case sockets and files every time you need to work with a "user-level"
|
||||
PHP file pointer.
|
||||
Streams take care of that and present the PHP extension coder with an ANSI
|
||||
stdio-alike API that looks much nicer and can be extended to support non file
|
||||
based data sources.
|
||||
|
||||
Using Streams
|
||||
=============
|
||||
Streams use a php_stream* parameter just as ANSI stdio (fread etc.) use a
|
||||
FILE* parameter.
|
||||
|
||||
The main functions are:
|
||||
|
||||
PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count);
|
||||
PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t
|
||||
count);
|
||||
PHPAPI size_t php_stream_printf(php_stream * stream TSRMLS_DC,
|
||||
const char * fmt, ...);
|
||||
PHPAPI int php_stream_eof(php_stream * stream);
|
||||
PHPAPI int php_stream_getc(php_stream * stream);
|
||||
PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen);
|
||||
PHPAPI int php_stream_close(php_stream * stream);
|
||||
PHPAPI int php_stream_flush(php_stream * stream);
|
||||
PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence);
|
||||
PHPAPI off_t php_stream_tell(php_stream * stream);
|
||||
PHPAPI int php_stream_lock(php_stream * stream, int mode);
|
||||
|
||||
These (should) behave in the same way as the ANSI stdio functions with similar
|
||||
names: fread, fwrite, fprintf, feof, fgetc, fgets, fclose, fflush, fseek, ftell, flock.
|
||||
|
||||
Opening Streams
|
||||
===============
|
||||
In most cases, you should use this API:
|
||||
|
||||
PHPAPI php_stream *php_stream_open_wrapper(char *path, char *mode,
|
||||
int options, char **opened_path TSRMLS_DC);
|
||||
|
||||
Where:
|
||||
path is the file or resource to open.
|
||||
mode is the stdio compatible mode eg: "wb", "rb" etc.
|
||||
options is a combination of the following values:
|
||||
IGNORE_PATH (default) - don't use include path to search for the file
|
||||
USE_PATH - use include path to search for the file
|
||||
IGNORE_URL - do not use plugin wrappers
|
||||
REPORT_ERRORS - show errors in a standard format if something
|
||||
goes wrong.
|
||||
STREAM_MUST_SEEK - If you really need to be able to seek the stream
|
||||
and don't need to be able to write to the original
|
||||
file/URL, use this option to arrange for the stream
|
||||
to be copied (if needed) into a stream that can
|
||||
be seek()ed.
|
||||
|
||||
opened_path is used to return the path of the actual file opened,
|
||||
but if you used STREAM_MUST_SEEK, may not be valid. You are
|
||||
responsible for efree()ing opened_path. opened_path may be (and usually
|
||||
is) NULL.
|
||||
|
||||
If you need to open a specific stream, or convert standard resources into
|
||||
streams there are a range of functions to do this defined in php_streams.h.
|
||||
A brief list of the most commonly used functions:
|
||||
|
||||
PHPAPI php_stream *php_stream_fopen_from_file(FILE *file, const char *mode);
|
||||
Convert a FILE * into a stream.
|
||||
|
||||
PHPAPI php_stream *php_stream_fopen_tmpfile(void);
|
||||
Open a FILE * with tmpfile() and convert into a stream.
|
||||
|
||||
PHPAPI php_stream *php_stream_fopen_temporary_file(const char *dir,
|
||||
const char *pfx, char **opened_path TSRMLS_DC);
|
||||
Generate a temporary file name and open it.
|
||||
|
||||
There are some network enabled relatives in php_network.h:
|
||||
|
||||
PHPAPI php_stream *php_stream_sock_open_from_socket(int socket, int persistent);
|
||||
Convert a socket into a stream.
|
||||
|
||||
PHPAPI php_stream *php_stream_sock_open_host(const char *host, unsigned short port,
|
||||
int socktype, int timeout, int persistent);
|
||||
Open a connection to a host and return a stream.
|
||||
|
||||
PHPAPI php_stream *php_stream_sock_open_unix(const char *path, int persistent,
|
||||
struct timeval *timeout);
|
||||
Open a UNIX domain socket.
|
||||
|
||||
|
||||
Stream Utilities
|
||||
================
|
||||
|
||||
If you need to copy some data from one stream to another, you will be please
|
||||
to know that the streams API provides a standard way to do this:
|
||||
|
||||
PHPAPI size_t php_stream_copy_to_stream(php_stream *src,
|
||||
php_stream *dest, size_t maxlen);
|
||||
|
||||
If you want to copy all remaining data from the src stream, pass
|
||||
PHP_STREAM_COPY_ALL as the maxlen parameter, otherwise maxlen indicates the
|
||||
number of bytes to copy.
|
||||
This function will try to use mmap where available to make the copying more
|
||||
efficient.
|
||||
|
||||
If you want to read the contents of a stream into an allocated memory buffer,
|
||||
you should use:
|
||||
|
||||
PHPAPI size_t php_stream_copy_to_mem(php_stream *src, char **buf,
|
||||
size_t maxlen, int persistent);
|
||||
|
||||
This function will set buf to the address of the buffer that it allocated,
|
||||
which will be maxlen bytes in length, or will be the entire length of the
|
||||
data remaining on the stream if you set maxlen to PHP_STREAM_COPY_ALL.
|
||||
The buffer is allocated using pemalloc(); you need to call pefree() to
|
||||
release the memory when you are done.
|
||||
As with copy_to_stream, this function will try use mmap where it can.
|
||||
|
||||
If you have an existing stream and need to be able to seek() it, you
|
||||
can use this function to copy the contents into a new stream that can
|
||||
be seek()ed:
|
||||
|
||||
PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream);
|
||||
|
||||
It returns one of the following values:
|
||||
#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */
|
||||
#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */
|
||||
#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */
|
||||
#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */
|
||||
|
||||
make_seekable will always set newstream to be the stream that is valid
|
||||
if the function succeeds.
|
||||
When you have finished, remember to close the stream.
|
||||
|
||||
NOTE: If you only need to seek forward, there is no need to call this
|
||||
function, as the php_stream_seek can emulate forward seeking when the
|
||||
whence parameter is SEEK_CUR.
|
||||
|
||||
NOTE: Writing to the stream may not affect the original source, so it
|
||||
only makes sense to use this for read-only use.
|
||||
|
||||
NOTE: If the origstream is network based, this function will block
|
||||
until the whole contents have been downloaded.
|
||||
|
||||
NOTE: Never call this function with an origstream that is referenced
|
||||
as a resource! It will close the origstream on success, and this
|
||||
can lead to a crash when the resource is later used/released.
|
||||
|
||||
NOTE: If you are opening a stream and need it to be seekable, use the
|
||||
STREAM_MUST_SEEK option to php_stream_open_wrapper();
|
||||
|
||||
PHPAPI int php_stream_supports_lock(php_stream * stream);
|
||||
|
||||
This function will return either 1 (success) or 0 (failure) indicating whether or
|
||||
not a lock can be set on this stream. Typically you can only set locks on stdio streams.
|
||||
|
||||
Casting Streams
|
||||
===============
|
||||
What if your extension needs to access the FILE* of a user level file pointer?
|
||||
You need to "cast" the stream into a FILE*, and this is how you do it:
|
||||
|
||||
FILE * fp;
|
||||
php_stream * stream; /* already opened */
|
||||
|
||||
if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void*)&fp, REPORT_ERRORS) == FAILURE) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
The prototype is:
|
||||
|
||||
PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int
|
||||
show_err);
|
||||
|
||||
The show_err parameter, if non-zero, will cause the function to display an
|
||||
appropriate error message of type E_WARNING if the cast fails.
|
||||
|
||||
castas can be one of the following values:
|
||||
PHP_STREAM_AS_STDIO - a stdio FILE*
|
||||
PHP_STREAM_AS_FD - a generic file descriptor
|
||||
PHP_STREAM_AS_SOCKETD - a socket descriptor
|
||||
|
||||
If you ask a socket stream for a FILE*, the abstraction will use fdopen to
|
||||
create it for you. Be warned that doing so may cause buffered data to be lost
|
||||
if you mix ANSI stdio calls on the FILE* with php stream calls on the stream.
|
||||
|
||||
If your system has the fopencookie function, php streams can synthesize a
|
||||
FILE* on top of any stream, which is useful for SSL sockets, memory based
|
||||
streams, data base streams etc. etc.
|
||||
|
||||
In situations where this is not desirable, you should query the stream
|
||||
to see if it naturally supports FILE *. You can use this code snippet
|
||||
for this purpose:
|
||||
|
||||
if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
|
||||
/* can safely cast to FILE* with no adverse side effects */
|
||||
}
|
||||
|
||||
You can use:
|
||||
|
||||
PHPAPI int php_stream_can_cast(php_stream * stream, int castas)
|
||||
|
||||
to find out if a stream can be cast, without actually performing the cast, so
|
||||
to check if a stream is a socket you might use:
|
||||
|
||||
if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS) {
|
||||
/* it can be a socket */
|
||||
}
|
||||
|
||||
Please note the difference between php_stream_is and php_stream_can_cast;
|
||||
stream_is tells you if the stream is a particular type of stream, whereas
|
||||
can_cast tells you if the stream can be forced into the form you request.
|
||||
The former doesn't change anything, while the later *might* change some
|
||||
state in the stream.
|
||||
|
||||
Stream Internals
|
||||
================
|
||||
|
||||
There are two main structures associated with a stream - the php_stream
|
||||
itself, which holds some state information (and possibly a buffer) and a
|
||||
php_stream_ops structure, which holds the "virtual method table" for the
|
||||
underlying implementation.
|
||||
|
||||
The php_streams ops struct consists of pointers to methods that implement
|
||||
read, write, close, flush, seek, gets and cast operations. Of these, an
|
||||
implementation need only implement write, read, close and flush. The gets
|
||||
method is intended to be used for streams if there is an underlying method
|
||||
that can efficiently behave as fgets. The ops struct also contains a label
|
||||
for the implementation that will be used when printing error messages - the
|
||||
stdio implementation has a label of "STDIO" for example.
|
||||
|
||||
The idea is that a stream implementation defines a php_stream_ops struct, and
|
||||
associates it with a php_stream using php_stream_alloc.
|
||||
|
||||
As an example, the php_stream_fopen() function looks like this:
|
||||
|
||||
PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
|
||||
{
|
||||
FILE * fp = fopen(filename, mode);
|
||||
php_stream * ret;
|
||||
|
||||
if (fp) {
|
||||
ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
php_stream_stdio_ops is a php_stream_ops structure that can be used to handle
|
||||
FILE* based streams.
|
||||
|
||||
A socket based stream would use code similar to that above to create a stream
|
||||
to be passed back to fopen_wrapper (or it's yet to be implemented successor).
|
||||
|
||||
The prototype for php_stream_alloc is this:
|
||||
|
||||
PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
|
||||
size_t bufsize, int persistent, const char * mode)
|
||||
|
||||
ops is a pointer to the implementation,
|
||||
abstract holds implementation specific data that is relevant to this instance
|
||||
of the stream,
|
||||
bufsize is the size of the buffer to use - if 0, then buffering at the stream
|
||||
level will be disabled (recommended for underlying sources that implement
|
||||
their own buffering - such a FILE*),
|
||||
persistent controls how the memory is to be allocated - persistently so that
|
||||
it lasts across requests, or non-persistently so that it is freed at the end
|
||||
of a request (it uses pemalloc),
|
||||
mode is the stdio-like mode of operation - php streams places no real meaning
|
||||
in the mode parameter, except that it checks for a 'w' in the string when
|
||||
attempting to write (this may change).
|
||||
|
||||
The mode parameter is passed on to fdopen/fopencookie when the stream is cast
|
||||
into a FILE*, so it should be compatible with the mode parameter of fopen().
|
||||
|
||||
Writing your own stream implementation
|
||||
======================================
|
||||
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
RULE #1: when writing your own streams: make sure you have configured PHP with
|
||||
--enable-debug.
|
||||
I've taken some great pains to hook into the Zend memory manager to help track
|
||||
down allocation problems. It will also help you spot incorrect use of the
|
||||
STREAMS_DC, STREAMS_CC and the semi-private STREAMS_REL_CC macros for function
|
||||
definitions.
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
RULE #2: Please use the stdio stream as a reference; it will help you
|
||||
understand the semantics of the stream operations, and it will always
|
||||
be more up to date than these docs :-)
|
||||
|
||||
First, you need to figure out what data you need to associate with the
|
||||
php_stream. For example, you might need a pointer to some memory for memory
|
||||
based streams, or if you were making a stream to read data from an RDBMS like
|
||||
MySQL, you might want to store the connection and rowset handles.
|
||||
|
||||
The stream has a field called abstract that you can use to hold this data.
|
||||
If you need to store more than a single field of data, define a structure to
|
||||
hold it, allocate it (use pemalloc with the persistent flag set
|
||||
appropriately), and use the abstract pointer to refer to it.
|
||||
|
||||
For structured state you might have this:
|
||||
|
||||
struct my_state {
|
||||
MYSQL conn;
|
||||
MYSQL_RES * result;
|
||||
};
|
||||
|
||||
struct my_state * state = pemalloc(sizeof(struct my_state), persistent);
|
||||
|
||||
/* initialize the connection, and run a query, using the fields in state to
|
||||
* hold the results */
|
||||
|
||||
state->result = mysql_use_result(&state->conn);
|
||||
|
||||
/* now allocate the stream itself */
|
||||
stream = php_stream_alloc(&my_ops, state, 0, persistent, "r");
|
||||
|
||||
/* now stream->abstract == state */
|
||||
|
||||
Once you have that part figured out, you can write your implementation and
|
||||
define the your own php_stream_ops struct (we called it my_ops in the above
|
||||
example).
|
||||
|
||||
For example, for reading from this weird MySQL stream:
|
||||
|
||||
static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
|
||||
{
|
||||
struct my_state * state = (struct my_state*)stream->abstract;
|
||||
|
||||
if (buf == NULL && count == 0) {
|
||||
/* in this special case, php_streams is asking if we have reached the
|
||||
* end of file */
|
||||
if (... at end of file ...)
|
||||
return EOF;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* pull out some data from the stream and put it in buf */
|
||||
... mysql_fetch_row(state->result) ...
|
||||
/* we could do something strange, like format the data as XML here,
|
||||
and place that in the buf, but that brings in some complexities,
|
||||
such as coping with a buffer size too small to hold the data,
|
||||
so I won't even go in to how to do that here */
|
||||
}
|
||||
|
||||
Implement the other operations - remember that write, read, close and flush
|
||||
are all mandatory. The rest are optional. Declare your stream ops struct:
|
||||
|
||||
php_stream_ops my_ops = {
|
||||
php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
|
||||
php_mysqlop_flush, NULL, NULL, NULL,
|
||||
"Strange MySQL example"
|
||||
}
|
||||
|
||||
Thats it!
|
||||
|
||||
Take a look at the STDIO implementation in streams.c for more information
|
||||
about how these operations work.
|
||||
The main thing to remember is that in your close operation you need to release
|
||||
and free the resources you allocated for the abstract field. In the case of
|
||||
the example above, you need to use mysql_free_result on the rowset, close the
|
||||
connection and then use pefree to dispose of the struct you allocated.
|
||||
You may read the stream->persistent field to determine if your struct was
|
||||
allocated in persistent mode or not.
|
||||
|
||||
vim:tw=78:et
|
||||
@@ -0,0 +1,182 @@
|
||||
Submitting Enhancements and Patches to PHP
|
||||
==========================================
|
||||
|
||||
This document describes how to submit an enhancement or patch for PHP.
|
||||
It's easy!
|
||||
|
||||
You don't need any login accounts or special access to download,
|
||||
build, debug and begin submitting PHP, PECL or PEAR code, tests or
|
||||
documentation. Once you've followed this README and had several
|
||||
patches accepted, commit privileges are often quickly granted.
|
||||
|
||||
An excellent article to read first is:
|
||||
http://phpadvent.org/2008/less-whining-more-coding-by-elizabeth-smith
|
||||
|
||||
|
||||
Online Forums
|
||||
-------------
|
||||
There are several IRC channels where PHP developers are often
|
||||
available to discuss questions. They include #php.pecl, #php.doc and
|
||||
#pear on the EFNet network and #php-dev-win on FreeNode.
|
||||
|
||||
|
||||
PHP Patches
|
||||
-----------
|
||||
If you are fixing broken functionality in PHP C source code first
|
||||
create a bug or identify an existing bug at http://bugs.php.net/. A
|
||||
bug can be used to track the patch progress and prevent your changes
|
||||
getting lost in the PHP mail archives.
|
||||
|
||||
If your change is large then create a Request For Comment (RFC) page
|
||||
on http://wiki.php.net/rfc, discuss it with the extension maintainer,
|
||||
and discuss it on the development mail list internals@lists.php.net.
|
||||
RFC Wiki accounts can be requested on
|
||||
http://wiki.php.net/start?do=register. PHP extension maintainers can
|
||||
be found in the EXTENSIONS file in the PHP source. Mail list
|
||||
subscription is explained on http://www.php.net/mailing-lists.php.
|
||||
|
||||
Information on PHP internal C functions is at
|
||||
http://www.php.net/internals, though this is considered incomplete.
|
||||
Various external resources can be found on the web. A standard
|
||||
printed reference is the book "Extending and Embedding PHP" by Sara
|
||||
Golemon.
|
||||
|
||||
Attach the patch to the PHP bug and consider sending a notification
|
||||
email about the change to internals@lists.php.net. Also CC the
|
||||
extension maintainer. Explain what has been changed by your patch.
|
||||
Test scripts should be included.
|
||||
|
||||
Please make the mail subject prefix "[PATCH]". If attaching a patch,
|
||||
ensure it has a file extension of ".txt". This is because only MIME
|
||||
attachments of type 'text/*' are accepted.
|
||||
|
||||
|
||||
PHP Documentation Patches
|
||||
-------------------------
|
||||
If you are fixing incorrect PHP documentation first create a bug or
|
||||
identify an existing bug at http://bugs.php.net/. A bug can be used
|
||||
to track the patch progress and prevent your changes getting lost in
|
||||
the PHP mail archives.
|
||||
|
||||
If your change is large, then first discuss it with the mail list
|
||||
phpdoc@lists.php.net. Subscription is explained on
|
||||
http://www.php.net/mailing-lists.php.
|
||||
|
||||
Information on contributing to PHP documentation is at
|
||||
http://php.net/dochowto and http://wiki.php.net/doc/howto
|
||||
|
||||
Attach the patch to the PHP bug and consider sending a notification
|
||||
email about the change to phpdoc@lists.php.net. Explain what has been
|
||||
fixed/added/changed by your patch.
|
||||
|
||||
Please make the mail subject prefix "[PATCH]". Include the bug id(s)
|
||||
which can be closed by your patch. If attaching a patch, ensure it
|
||||
has a file extension of ".txt". This is because only MIME attachments
|
||||
of type 'text/*' are accepted.
|
||||
|
||||
|
||||
PECL Extension Patches: http://pecl.php.net/
|
||||
--------------------------------------------
|
||||
If you are fixing broken functionality in a PECL extension then create
|
||||
a bug or identify an existing bug at http://pecl.php.net/bugs/. A bug
|
||||
can be used to track the patch progress and prevent your changes
|
||||
getting lost in the PHP mail archives.
|
||||
|
||||
If your change is large then create a Request For Comment (RFC) page
|
||||
on http://wiki.php.net/rfc, discuss it with the extension maintainer,
|
||||
and discuss it on the development mail list pecl-dev@lists.php.net.
|
||||
PECL mail list subscription is explained on
|
||||
http://pecl.php.net/support.php. RFC Wiki accounts can be requested
|
||||
on http://wiki.php.net/start?do=register
|
||||
|
||||
Information on PHP internal C functions is at
|
||||
http://www.php.net/internals, though this is considered incomplete.
|
||||
Various external resources can be found on the web. A standard
|
||||
printed reference is the book "Extending and Embedding PHP" by Sara
|
||||
Golemon.
|
||||
|
||||
Update any open bugs and add a link to the source of your patch. Send
|
||||
the patch or pointer to the bug to pecl-dev@lists.php.net. Also CC
|
||||
the extension maintainer. Explain what has been changed by your
|
||||
patch. Test scripts should be included.
|
||||
|
||||
Please make the mail subject prefix "[PATCH] ...". Include the patch
|
||||
as an attachment with a file extension of ".txt". This is because
|
||||
only MIME attachments of type 'text/*' are accepted.
|
||||
|
||||
|
||||
PEAR Package Patches: http://pear.php.net/
|
||||
------------------------------------------
|
||||
Information on contributing to PEAR is available at
|
||||
http://pear.php.net/manual/en/developers-newmaint.php and
|
||||
http://pear.php.net/manual/en/guide-developers.php
|
||||
|
||||
|
||||
How to create your PHP, PHP Documentation or PECL patch
|
||||
-------------------------------------------------------
|
||||
PHP and PECL use Subversion (SVN) for revision control. Read
|
||||
http://www.php.net/svn.php for help on using SVN to get and build PHP
|
||||
source code. We recommend using a Sparse Directory checkout described
|
||||
in http://wiki.php.net/vcs/svnfaq. If you are new to SVN, read
|
||||
http://svnbook.red-bean.com.
|
||||
|
||||
Generally we ask that bug fix patches work on the current stable PHP
|
||||
development branches and on "trunk". New PHP features only need to
|
||||
work on "trunk".
|
||||
|
||||
Read CODING_STANDARDS before you start working.
|
||||
|
||||
After modifying the source see README.TESTING and
|
||||
http://qa.php.net/write-test.php for how to test. Submitting test
|
||||
scripts helps us to understand what functionality has changed. It is
|
||||
important for the stability and maintainability of PHP that tests are
|
||||
comprehensive.
|
||||
|
||||
After testing is finished, create a patch file using the command:
|
||||
|
||||
svn diff > your_patch.txt
|
||||
|
||||
For ease of review and later troubleshooting, submit individual
|
||||
patches for each bug or feature.
|
||||
|
||||
|
||||
Checklist for submitting your PHP or PECL code patch
|
||||
----------------------------------------------------
|
||||
- Update SVN source just before running your final 'diff' and
|
||||
before testing.
|
||||
- Add in-line comments and/or have external documentation ready.
|
||||
Use only "/* */" style comments, not "//".
|
||||
- Create test scripts for use with "make test".
|
||||
- Run "make test" to check your patch doesn't break other features.
|
||||
- Rebuild PHP with --enable-debug (which will show some kinds of
|
||||
memory errors) and check the PHP and web server error logs after
|
||||
running your PHP tests.
|
||||
- Rebuild PHP with --enable-maintainer-zts to check your patch
|
||||
compiles on multi-threaded web servers.
|
||||
- Review the patch once more just before submitting it.
|
||||
|
||||
|
||||
What happens after submitting your PHP, PHP Documentation or PECL patch
|
||||
-----------------------------------------------------------------------
|
||||
If your patch is easy to review and obviously has no side-effects,
|
||||
it might be committed relatively quickly.
|
||||
|
||||
Because PHP is a volunteer-driven effort more complex patches will
|
||||
require patience on your side. If you do not receive feedback in a
|
||||
few days, consider resubmitting the patch. Before doing this think
|
||||
about these questions:
|
||||
|
||||
- Did I send the patch to the right mail list?
|
||||
- Did I review the mail list archives to see if these kind of
|
||||
changes had been discussed before?
|
||||
- Did I explain my patch clearly?
|
||||
- Is my patch too hard to review? Because of what factors?
|
||||
|
||||
|
||||
What happens when your PHP or PECL patch is applied
|
||||
---------------------------------------------------
|
||||
Your name will likely be included in the SVN commit log. If your
|
||||
patch affects end users, a brief description and your name might be
|
||||
added to the NEWS file.
|
||||
|
||||
Thank you for patching PHP!
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
[IMPORTANT NOTICE]
|
||||
------------------
|
||||
Failed tests usualy indicate a problem with your local system setup
|
||||
and not within PHP itself (at least for official PHP release versions).
|
||||
You may decide to automaticaly submit a test summary to our QA workflow
|
||||
at the end of a test run.
|
||||
Please do *not* submit a failed test as a bug or ask for help on why
|
||||
it failed on your system without providing substantial backup information
|
||||
on *why* the test failed on your special setup. Thank you :-)
|
||||
|
||||
|
||||
[Testing Basics]
|
||||
----------------
|
||||
The easiest way to test your PHP build is to run "make test" from the
|
||||
command line after successfully compiling. This will run the complete
|
||||
tests for all enabled functionalities and extensions using the PHP
|
||||
CLI binary.
|
||||
To execute test scripts, you must build PHP with some SAPI, then you
|
||||
type "make test" to execute all or some test scripts saved under
|
||||
"tests" directory under source root directory.
|
||||
|
||||
Usage:
|
||||
make test
|
||||
|
||||
"make test" basically executes "run-tests.php" script
|
||||
under the source root (parallel builds will not work). Therefore you
|
||||
can execute the script as follows:
|
||||
|
||||
TEST_PHP_EXECUTABLE=sapi/cli/php \
|
||||
sapi/cli/php [-c /path/to/php.ini] run-tests.php [ext/foo/tests/GLOB]
|
||||
|
||||
|
||||
[Which "php" executable "make test" look for]
|
||||
---------------------------------------------
|
||||
If you are running the run-tests.php script from the command line (as above)
|
||||
you must set the TEST_PHP_EXECUTABLE environment variable to explicitly
|
||||
select the PHP executable that is to be tested, that is, used to run the test scripts.
|
||||
|
||||
If you run the tests using make test, the PHP CLI and CGI executables are
|
||||
automatically set for you. "make test" executes "run-tests.php" script with the CLI binary. Some
|
||||
test scripts such as session must be executed by CGI SAPI. Therefore,
|
||||
you must build PHP with CGI SAPI to perform all tests.
|
||||
|
||||
NOTE: PHP binary executing "run-tests.php" and php binary used for
|
||||
executing test scripts may differ. If you use different PHP binary for
|
||||
executing "run-tests.php" script, you may get errors.
|
||||
|
||||
|
||||
[Which php.ini is used]
|
||||
-----------------------
|
||||
"make test" uses the same php.ini file as it would once installed.
|
||||
The tests have been written to be independent of that php.ini file,
|
||||
so if you find a test that is affected by a setting, please report
|
||||
this, so we can address the issue.
|
||||
|
||||
|
||||
[Which test scripts are executed]
|
||||
---------------------------------
|
||||
"run-tests.php" ("make test"), without any arguments executes all
|
||||
test scripts by extracting all directories named "tests"
|
||||
from the source root and any subdirectories below. If there are files,
|
||||
which have a "phpt" extension, "run-tests.php" looks at the sections
|
||||
in these files, determines whether it should run it, by evaluating
|
||||
the 'SKIP' section. If the test is eligible for execution, the 'FILE'
|
||||
section is extracted into a ".php" file (with the same name besides
|
||||
the extension) and gets executed.
|
||||
When an argument is given or TESTS environment variable is set, the
|
||||
GLOB is expanded by the shell and any file with extension "*.phpt" is
|
||||
regarded as a test file.
|
||||
|
||||
Tester can easily execute tests selectively with as follows.
|
||||
|
||||
Examples:
|
||||
./sapi/cli/php run-tests.php ext/mbstring/*
|
||||
./sapi/cli/php run-tests.php ext/mbstring/020.phpt
|
||||
|
||||
|
||||
[Test results]
|
||||
--------------
|
||||
Test results are printed to standard output. If there is a failed test,
|
||||
the "run-tests.php" script saves the result, the expected result and the
|
||||
code executed to the test script directory. For example, if
|
||||
ext/myext/tests/myext.phpt fails to pass, the following files are created:
|
||||
|
||||
ext/myext/tests/myext.php - actual test file executed
|
||||
ext/myext/tests/myext.log - log of test execution (L)
|
||||
ext/myext/tests/myext.exp - expected output (E)
|
||||
ext/myext/tests/myext.out - output from test script (O)
|
||||
ext/myext/tests/myext.diff - diff of .out and .exp (D)
|
||||
|
||||
Failed tests are always bugs. Either the test is bugged or not considering
|
||||
factors applying to the tester's environment, or there is a bug in PHP.
|
||||
If this is a known bug, we strive to provide bug numbers, in either the
|
||||
test name or the file name. You can check the status of such a bug, by
|
||||
going to: http://bugs.php.net/12345 where 12345 is the bug number.
|
||||
For clarity and automated processing, bug numbers are prefixed by a hash
|
||||
sign '#' in test names and/or test cases are named bug12345.phpt.
|
||||
|
||||
NOTE: The files generated by tests can be selected by setting the
|
||||
environment variable TEST_PHP_LOG_FORMAT. For each file you want to be
|
||||
generated use the character in brackets as shown above (default is LEOD).
|
||||
The php file will be generated always.
|
||||
|
||||
NOTE: You can set environment variable TEST_PHP_DETAILED to enable
|
||||
detailed test information.
|
||||
|
||||
[Automated testing]
|
||||
If you like to keep up to speed, with latest developments and quality
|
||||
assurance, setting the environment variable NO_INTERACTION to 1, will not
|
||||
prompt the tester for any user input.
|
||||
|
||||
Normally, the exit status of "make test" is zero, regardless of the results
|
||||
of independent tests. Set the environment variable REPORT_EXIT_STATUS to 1,
|
||||
and "make test" will set the exit status ("$?") to non-zero, when an
|
||||
individual test has failed.
|
||||
|
||||
Example script to be run by cron(1):
|
||||
========== qa-test.sh =============
|
||||
#!/bin/sh
|
||||
|
||||
CO_DIR=$HOME/cvs/php5
|
||||
MYMAIL=qa-test@domain.com
|
||||
TMPDIR=/var/tmp
|
||||
TODAY=`date +"%Y%m%d"`
|
||||
|
||||
# Make sure compilation enviroment is correct
|
||||
CONFIGURE_OPTS='--disable-all --enable-cli --with-pcre'
|
||||
export MAKE=gmake
|
||||
export CC=gcc
|
||||
|
||||
# Set test environment
|
||||
export NO_INTERACTION=1
|
||||
export REPORT_EXIT_STATUS=1
|
||||
|
||||
cd $CO_DIR
|
||||
cvs update . >>$TMPDIR/phpqatest.$TODAY
|
||||
./cvsclean ; ./buildconf ; ./configure $CONFIGURE_OPTS ; $MAKE
|
||||
$MAKE test >>$TMPDIR/phpqatest.$TODAY 2>&1
|
||||
if test $? -gt 0
|
||||
then
|
||||
cat $TMPDIR/phpqatest.$TODAY | mail -s"PHP-QA Test Failed for $TODAY" $MYMAIL
|
||||
fi
|
||||
========== end of qa-test.sh =============
|
||||
|
||||
NOTE: the exit status of run-tests.php will be 1 when
|
||||
REPORT_EXIT_STATUS is set. The result of "make test" may be higher
|
||||
than that. At present, gmake 3.79.1 returns 2, so it is
|
||||
advised to test for non-zero, rather then a specific value.
|
||||
|
||||
|
||||
[Creating new test files]
|
||||
-------------------------
|
||||
Writing test file is very easy if you are used to PHP.
|
||||
See the HOWTO at http://qa.php.net/write-test.php
|
||||
|
||||
|
||||
[How to help us]
|
||||
----------------
|
||||
If you find bug in PHP, you can submit bug report AND test script
|
||||
for us. You don't have to write complete script, just give us test
|
||||
script with following format. Please test the script and make sure
|
||||
you write the correct ACTUAL OUTPUT and EXPECTED OUTPUT before you
|
||||
submit.
|
||||
|
||||
<?php
|
||||
/*
|
||||
Bug #12345
|
||||
substr() bug. Do not return expected string.
|
||||
|
||||
ACTUAL OUTPUT
|
||||
XYXA
|
||||
|
||||
EXPECTED OUTPUT
|
||||
ABCD
|
||||
*/
|
||||
|
||||
$str = "XYZABCD";
|
||||
echo substr($str,3,7);
|
||||
|
||||
?>
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
[IMPORTANT NOTICE]
|
||||
------------------
|
||||
This is an addendum to README.TESTING with additional information
|
||||
specific to server-tests.php.
|
||||
|
||||
server-tests.php is backward compatible with tests developed for
|
||||
the original run-tests.php script. server-tests is *not* used by
|
||||
'make test'. server-tests was developed to provide support for
|
||||
testing PHP under it's primary environment, HTTP, and can run the
|
||||
PHP tests under any of the SAPI modules that are direct executables,
|
||||
or are accessable via HTTP.
|
||||
|
||||
[New features]
|
||||
----------------
|
||||
* Command line interface:
|
||||
You can run 'php server-tests.php -h' to get all the possible options.
|
||||
* Configuration file:
|
||||
the -c argument will allow you to use a configuration file. This is
|
||||
handy if you are testing multiple environments and need various options
|
||||
depending on the environment.
|
||||
see server-tests-config.php for details.
|
||||
* CGI Emulation:
|
||||
Will emulate a CGI environment when testing with the cgi sapi executable.
|
||||
* HTTP testing:
|
||||
can be configured to run test scripts through an HTTP server running
|
||||
on localhost. localhost is required since either the web server must
|
||||
alias a directory to the php source directory, or the test scripts
|
||||
must be copied to a directory under the web server
|
||||
(see config options TEST_WEB_BASE_URL, TEST_BASE_PATH, and TEST_WEB_EXT)
|
||||
* New sections supported for test files (see below)
|
||||
|
||||
When running tests over http, tests that require ini settings different that what
|
||||
the web server runs under will be skipped. Since the test harness defines a number
|
||||
of ini settings by default, the web server may require special configuration to
|
||||
make testing work.
|
||||
|
||||
[Example Usage]
|
||||
----------------
|
||||
Some (but not all!) examples of usage:
|
||||
|
||||
1. run tests from the php source directory
|
||||
php server-tests.php -p /path/to/php-cli
|
||||
|
||||
2. run tests using cgi emulation
|
||||
php server-tests.php -p /path/to/php-cgi
|
||||
|
||||
3. run tests over http, copying test files into document root
|
||||
php server-tests.php -w -u http://localhost/test -m /path/to/htdocs/test
|
||||
|
||||
4. run tests over http, php sources have been aliased in web server
|
||||
php server-tests.php -w -u http://localhost/test
|
||||
|
||||
5. run tests using configuration file
|
||||
php server-tests.php -c /path/to/server-tests-config.php
|
||||
|
||||
6. run tests using configuration file, but overriding some settings:
|
||||
(config file must be first)
|
||||
php server-tests.php -c /path/to/server-tests-config.php -w -t 3 -d /path/to/testdir
|
||||
|
||||
NOTE: configuration as described in README.TESTING still works.
|
||||
|
||||
[New Test Sections]
|
||||
----------------
|
||||
In addition to the traditional test sections
|
||||
(see http://qa.php.net/write-test.php), several new sections are available
|
||||
under server-tests.
|
||||
|
||||
--POST--
|
||||
This is not a new section, but not multipart posts are supported for testing
|
||||
file uploads, or other types of POST data.
|
||||
|
||||
--CGI--
|
||||
This section takes no value. It merely provides a simple marker for tests
|
||||
that MUST be run as CGI, even if there is no --POST-- or --GET-- sections
|
||||
in the test file.
|
||||
|
||||
--DESCRIPTION--
|
||||
Not used for anything, just a section for documenting the test
|
||||
|
||||
--ENV--
|
||||
This section get's eval()'d to help build an environment for the
|
||||
execution of the test. This can be used to change environment
|
||||
vars that are used for CGI emulation, or simply to set env vars
|
||||
for cli testing. A full example looks like:
|
||||
|
||||
--ENV--
|
||||
return <<<END
|
||||
PATH_TRANSLATED=$filename
|
||||
PATH_INFO=$scriptname
|
||||
SCRIPT_NAME=$scriptname
|
||||
END;
|
||||
|
||||
Some variables are made easily available for use in this section, they
|
||||
include:
|
||||
$filename full native path to file, will become PATH_TRANSLATED
|
||||
$filepath =dirname($filename)
|
||||
$scriptname this is what will become SCRIPT_NAME unless you override it
|
||||
$docroot the equivelant of DOCUMENT_ROOT under Apache
|
||||
$cwd the directory that the test is being initiated from
|
||||
$this->conf all server-tests configuration vars
|
||||
$this->env all environment variables that will get passed to the test
|
||||
|
||||
|
||||
--REQUEST--
|
||||
This section is also eval'd, and is similar in nature to --ENV--. However,
|
||||
this section is used to build the url used in an HTTP request. Valid values
|
||||
to set in this section would include:
|
||||
SCRIPT_NAME The inital part of the request url
|
||||
PATH_INFO The pathinfo part of a request url
|
||||
FRAGMENT The fragment section of a url (after #)
|
||||
QUERY_STRING The query part of a url (after ?)
|
||||
|
||||
--REQUEST--
|
||||
return <<<END
|
||||
PATH_INFO=/path/info
|
||||
END;
|
||||
|
||||
--HEADERS--
|
||||
This section is also eval'd. It is used to provide additional headers sent
|
||||
in an HTTP request, such as content type for multipart posts, cookies, etc.
|
||||
|
||||
--HEADERS--
|
||||
return <<<END
|
||||
Content-Type=multipart/form-data; boundary=---------------------------240723202011929
|
||||
Content-Length=100
|
||||
END;
|
||||
|
||||
--EXPECTHEADERS--
|
||||
This section can be used to define what headers are required to be
|
||||
received back from a request, and is checked in addition to the
|
||||
regular expect sections. For example:
|
||||
|
||||
--EXPECTHEADERS--
|
||||
Status: 404
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
PHP Build System V5 Overview
|
||||
|
||||
- supports Makefile.ins during transition phase
|
||||
- not-really-portable Makefile includes have been eliminated
|
||||
- supports separate build directories without VPATH by using
|
||||
explicit rules only
|
||||
- does not waste disk-space/CPU-time for building temporary libraries
|
||||
=> especially noticeable on slower systems
|
||||
- slow recursive make replaced with one global Makefile
|
||||
- eases integration of proper dependencies
|
||||
- adds PHP_DEFINE(what[, value]) which creates a single include-file
|
||||
per what. This will allow more fine-grained dependencies.
|
||||
- abandoning the "one library per directory" concept
|
||||
- improved integration of the CLI
|
||||
- several new targets
|
||||
build-modules: builds and copies dynamic modules into modules/
|
||||
install-cli: installs the CLI only, so that the install-sapi
|
||||
target does only what its name says
|
||||
- finally abandoned automake (still requires aclocal at this time)
|
||||
- changed some configure-time constructs to run at buildconf-time
|
||||
- upgraded shtool to 1.5.4
|
||||
- removed $(moduledir) (use EXTENSION_DIR)
|
||||
|
||||
The Reason For a New System
|
||||
|
||||
It became more and more apparent that there is a severe need
|
||||
for addressing the portability concerns and improving the chance
|
||||
that your build is correct (how often have you been told to
|
||||
"make clean"? When this is done, you won't need to anymore).
|
||||
|
||||
|
||||
If You Build PHP on a Unix System
|
||||
|
||||
|
||||
You, as a user of PHP, will notice no changes. Of course, the build
|
||||
system will be faster, look better and work smarter.
|
||||
|
||||
|
||||
|
||||
If You Are Developing PHP
|
||||
|
||||
|
||||
|
||||
|
||||
Extension developers:
|
||||
|
||||
Makefile.ins are abandoned. The files which are to be compiled
|
||||
are specified in the config.m4 now using the following macro:
|
||||
|
||||
PHP_NEW_EXTENSION(foo, foo.c bar.c baz.cpp, $ext_shared)
|
||||
|
||||
E.g. this enables the extension foo which consists of three source-code
|
||||
modules, two in C and one in C++. And, depending on the user's wishes,
|
||||
the extension will even be built as a dynamic module.
|
||||
|
||||
The full syntax:
|
||||
|
||||
PHP_NEW_EXTENSION(extname, sources [, shared [,sapi_class[, extra-cflags]]])
|
||||
|
||||
Please have a look at acinclude.m4 for the gory details and meanings
|
||||
of the other parameters.
|
||||
|
||||
And that's basically it for the extension side.
|
||||
|
||||
If you previously built sub-libraries for this module, add
|
||||
the source-code files here as well. If you need to specify
|
||||
separate include directories, do it this way:
|
||||
|
||||
PHP_NEW_EXTENSION(foo, foo.c mylib/bar.c mylib/gregor.c,,,-I@ext_srcdir@/lib)
|
||||
|
||||
E.g. this builds the three files which are located relative to the
|
||||
extension source directory and compiles all three files with the
|
||||
special include directive (@ext_srcdir@ is automatically replaced).
|
||||
|
||||
Now, you need to tell the build system that you want to build files
|
||||
in a directory called $ext_builddir/lib:
|
||||
|
||||
PHP_ADD_BUILD_DIR($ext_builddir/lib)
|
||||
|
||||
Make sure to call this after PHP_NEW_EXTENSION, because $ext_builddir
|
||||
is only set by the latter.
|
||||
|
||||
If you have a complex extension, you might to need add special
|
||||
Make rules. You can do this by calling PHP_ADD_MAKEFILE_FRAGMENT
|
||||
in your config.m4 after PHP_NEW_EXTENSION.
|
||||
|
||||
This will read a file in the source-dir of your extension called
|
||||
Makefile.frag. In this file, $(builddir) and $(srcdir) will be
|
||||
replaced by the values which are correct for your extension
|
||||
and which are again determined by the PHP_NEW_EXTENSION macro.
|
||||
|
||||
Make sure to prefix *all* relative paths correctly with either
|
||||
$(builddir) or $(srcdir). Because the build system does not
|
||||
change the working directory anymore, we must use either
|
||||
absolute paths or relative ones to the top build-directory.
|
||||
Correct prefixing ensures that.
|
||||
|
||||
|
||||
SAPI developers:
|
||||
|
||||
Instead of using PHP_SAPI=foo/PHP_BUILD_XYZ, you will need to type
|
||||
|
||||
PHP_SELECT_SAPI(name, type, sources.c)
|
||||
|
||||
I.e. specify the source-code files as above and also pass the
|
||||
information regarding how PHP is supposed to be built (shared
|
||||
module, program, etc).
|
||||
|
||||
For example for APXS:
|
||||
|
||||
PHP_SELECT_SAPI(apache, shared, sapi_apache.c mod_php5.c php_apache.c)
|
||||
|
||||
|
||||
|
||||
General info
|
||||
|
||||
The foundation for the new system is the flexible handling of
|
||||
sources and their contexts. With the help of macros you
|
||||
can define special flags for each source-file, where it is
|
||||
located, in which target context it can work, etc.
|
||||
|
||||
Have a look at the well documented macros
|
||||
PHP_ADD_SOURCES(_X) in acinclude.m4.
|
||||
@@ -0,0 +1,6 @@
|
||||
The Win32 Build System.
|
||||
|
||||
See http://wiki.php.net/internals/windows/stepbystepbuild
|
||||
|
||||
vim:tw=78:sw=1:ts=1:et
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
Input Filter Support in PHP 5
|
||||
-----------------------------
|
||||
|
||||
XSS (Cross Site Scripting) hacks are becoming more and more prevalent,
|
||||
and can be quite difficult to prevent. Whenever you accept user data
|
||||
and somehow display this data back to users, you are likely vulnerable
|
||||
to XSS hacks.
|
||||
|
||||
The Input Filter support in PHP 5 is aimed at providing the framework
|
||||
through which a company-wide or site-wide security policy can be
|
||||
enforced. It is implemented as a SAPI hook and is called from the
|
||||
treat_data and post handler functions. To implement your own security
|
||||
policy you will need to write a standard PHP extension. There is also
|
||||
a powerful standard implementation in ext/filter that should suit most
|
||||
peoples' needs. However, if you want to implement your own security
|
||||
policy, read on.
|
||||
|
||||
A simple implementation might look like the following. This stores the
|
||||
original raw user data and adds a my_get_raw() function while the normal
|
||||
$_POST, $_GET and $_COOKIE arrays are only populated with stripped
|
||||
data. In this simple example all I am doing is calling strip_tags() on
|
||||
the data.
|
||||
|
||||
ZEND_BEGIN_MODULE_GLOBALS(my_input_filter)
|
||||
zval *post_array;
|
||||
zval *get_array;
|
||||
zval *cookie_array;
|
||||
ZEND_END_MODULE_GLOBALS(my_input_filter)
|
||||
|
||||
#ifdef ZTS
|
||||
#define IF_G(v) TSRMG(my_input_filter_globals_id, zend_my_input_filter_globals *, v)
|
||||
#else
|
||||
#define IF_G(v) (my_input_filter_globals.v)
|
||||
#endif
|
||||
|
||||
ZEND_DECLARE_MODULE_GLOBALS(my_input_filter)
|
||||
|
||||
zend_function_entry my_input_filter_functions[] = {
|
||||
PHP_FE(my_get_raw, NULL)
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
zend_module_entry my_input_filter_module_entry = {
|
||||
STANDARD_MODULE_HEADER,
|
||||
"my_input_filter",
|
||||
my_input_filter_functions,
|
||||
PHP_MINIT(my_input_filter),
|
||||
PHP_MSHUTDOWN(my_input_filter),
|
||||
NULL,
|
||||
PHP_RSHUTDOWN(my_input_filter),
|
||||
PHP_MINFO(my_input_filter),
|
||||
"0.1",
|
||||
STANDARD_MODULE_PROPERTIES
|
||||
};
|
||||
|
||||
PHP_MINIT_FUNCTION(my_input_filter)
|
||||
{
|
||||
ZEND_INIT_MODULE_GLOBALS(my_input_filter, php_my_input_filter_init_globals, NULL);
|
||||
|
||||
REGISTER_LONG_CONSTANT("POST", PARSE_POST, CONST_CS | CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("GET", PARSE_GET, CONST_CS | CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("COOKIE", PARSE_COOKIE, CONST_CS | CONST_PERSISTENT);
|
||||
|
||||
sapi_register_input_filter(my_sapi_input_filter);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
PHP_RSHUTDOWN_FUNCTION(my_input_filter)
|
||||
{
|
||||
if(IF_G(get_array)) {
|
||||
zval_ptr_dtor(&IF_G(get_array));
|
||||
IF_G(get_array) = NULL;
|
||||
}
|
||||
if(IF_G(post_array)) {
|
||||
zval_ptr_dtor(&IF_G(post_array));
|
||||
IF_G(post_array) = NULL;
|
||||
}
|
||||
if(IF_G(cookie_array)) {
|
||||
zval_ptr_dtor(&IF_G(cookie_array));
|
||||
IF_G(cookie_array) = NULL;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
PHP_MINFO_FUNCTION(my_input_filter)
|
||||
{
|
||||
php_info_print_table_start();
|
||||
php_info_print_table_row( 2, "My Input Filter Support", "enabled" );
|
||||
php_info_print_table_row( 2, "Revision", "$Id$");
|
||||
php_info_print_table_end();
|
||||
}
|
||||
|
||||
/* The filter handler. If you return 1 from it, then PHP also registers the
|
||||
* (modified) variable. Returning 0 prevents PHP from registering the variable;
|
||||
* you can use this if your filter already registers the variable under a
|
||||
* different name, or if you just don't want the variable registered at all. */
|
||||
SAPI_INPUT_FILTER_FUNC(my_sapi_input_filter)
|
||||
{
|
||||
zval new_var;
|
||||
zval *array_ptr = NULL;
|
||||
char *raw_var;
|
||||
int var_len;
|
||||
|
||||
assert(*val != NULL);
|
||||
|
||||
switch(arg) {
|
||||
case PARSE_GET:
|
||||
if(!IF_G(get_array)) {
|
||||
ALLOC_ZVAL(array_ptr);
|
||||
array_init(array_ptr);
|
||||
INIT_PZVAL(array_ptr);
|
||||
}
|
||||
IF_G(get_array) = array_ptr;
|
||||
break;
|
||||
case PARSE_POST:
|
||||
if(!IF_G(post_array)) {
|
||||
ALLOC_ZVAL(array_ptr);
|
||||
array_init(array_ptr);
|
||||
INIT_PZVAL(array_ptr);
|
||||
}
|
||||
IF_G(post_array) = array_ptr;
|
||||
break;
|
||||
case PARSE_COOKIE:
|
||||
if(!IF_G(cookie_array)) {
|
||||
ALLOC_ZVAL(array_ptr);
|
||||
array_init(array_ptr);
|
||||
INIT_PZVAL(array_ptr);
|
||||
}
|
||||
IF_G(cookie_array) = array_ptr;
|
||||
break;
|
||||
}
|
||||
Z_STRLEN(new_var) = val_len;
|
||||
Z_STRVAL(new_var) = estrndup(*val, val_len);
|
||||
Z_TYPE(new_var) = IS_STRING;
|
||||
|
||||
var_len = strlen(var);
|
||||
raw_var = emalloc(var_len+5); /* RAW_ and a \0 */
|
||||
strcpy(raw_var, "RAW_");
|
||||
strlcat(raw_var,var,var_len+5);
|
||||
|
||||
php_register_variable_ex(raw_var, &new_var, array_ptr TSRMLS_DC);
|
||||
|
||||
php_strip_tags(*val, val_len, NULL, NULL, 0);
|
||||
|
||||
*new_val_len = strlen(*val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
PHP_FUNCTION(my_get_raw)
|
||||
{
|
||||
long arg;
|
||||
char *var;
|
||||
int var_len;
|
||||
zval **tmp;
|
||||
zval *array_ptr = NULL;
|
||||
|
||||
if(zend_parse_parameters(2 TSRMLS_CC, "ls", &arg, &var, &var_len) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(arg) {
|
||||
case PARSE_GET:
|
||||
array_ptr = IF_G(get_array);
|
||||
break;
|
||||
case PARSE_POST:
|
||||
array_ptr = IF_G(post_array);
|
||||
break;
|
||||
case PARSE_COOKIE:
|
||||
array_ptr = IF_G(post_array);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!array_ptr) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
if(zend_hash_find(HASH_OF(array_ptr), var, var_len+5, (void **)&tmp) == SUCCESS) {
|
||||
*return_value = **tmp;
|
||||
zval_copy_ctor(return_value);
|
||||
} else {
|
||||
RETVAL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,144 +1,30 @@
|
||||
<div align="center">
|
||||
<a href="https://php.net">
|
||||
<img
|
||||
alt="PHP"
|
||||
src="https://www.php.net/images/logos/new-php-logo.svg"
|
||||
width="150">
|
||||
</a>
|
||||
</div>
|
||||
The PHP Interpreter
|
||||
===================
|
||||
|
||||
# The PHP Interpreter
|
||||
This is the github mirror of the official PHP repository located at
|
||||
http://git.php.net.
|
||||
|
||||
PHP is a popular general-purpose scripting language that is especially suited to
|
||||
web development. Fast, flexible and pragmatic, PHP powers everything from your
|
||||
blog to the most popular websites in the world. PHP is distributed under the
|
||||
[PHP License v3.01](LICENSE).
|
||||
[](http://travis-ci.org/php/php-src)
|
||||
|
||||
[](https://travis-ci.org/php/php-src)
|
||||
[](https://ci.appveyor.com/project/php/php-src)
|
||||
[](https://dev.azure.com/phpazuredevops/php/_build/latest?definitionId=1&branchName=master)
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:php)
|
||||
Pull Requests
|
||||
=============
|
||||
PHP accepts pull requests via github. Discussions are done on github, but
|
||||
depending on the topic can also be relayed to the official PHP developer
|
||||
mailinglist internals@lists.php.net.
|
||||
|
||||
## Documentation
|
||||
New features require an RFC and must be accepted by the developers.
|
||||
See https://wiki.php.net/rfc and https://wiki.php.net/rfc/voting for more
|
||||
information on the process.
|
||||
|
||||
The PHP manual is available at [php.net/docs](https://php.net/docs).
|
||||
|
||||
## Installation
|
||||
|
||||
### Prebuilt packages and binaries
|
||||
|
||||
Prebuilt packages and binaries can be used to get up and running fast with PHP.
|
||||
|
||||
For Windows, the PHP binaries can be obtained from
|
||||
[windows.php.net](https://windows.php.net). After extracting the archive the
|
||||
`*.exe` files are ready to use.
|
||||
|
||||
For other systems, see the [installation chapter](https://php.net/install).
|
||||
|
||||
### Building PHP source code
|
||||
|
||||
*For Windows, see [Build your own PHP on Windows](https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2).*
|
||||
|
||||
For a minimal PHP build from Git, you will need autoconf, bison, and re2c. For
|
||||
a default build, you will additionally need libxml2 and libsqlite3.
|
||||
|
||||
On Ubuntu, you can install these using:
|
||||
|
||||
sudo apt install -y pkg-config build-essential autoconf bison re2c \
|
||||
libxml2-dev libsqlite3-dev
|
||||
|
||||
On Fedora, you can install these using:
|
||||
|
||||
sudo dnf install re2c bison autoconf make libtool ccache libxml2-devel sqlite-devel
|
||||
|
||||
Generate configure:
|
||||
|
||||
./buildconf
|
||||
|
||||
Configure your build. `--enable-debug` is recommended for development, see
|
||||
`./configure --help` for a full list of options.
|
||||
|
||||
# For development
|
||||
./configure --enable-debug
|
||||
# For production
|
||||
./configure
|
||||
|
||||
Build PHP. To speed up the build, specify the maximum number of jobs using `-j`:
|
||||
|
||||
make -j4
|
||||
|
||||
The number of jobs should usually match the number of available cores, which
|
||||
can be determined using `nproc`.
|
||||
|
||||
## Testing PHP source code
|
||||
|
||||
PHP ships with an extensive test suite, the command `make test` is used after
|
||||
successful compilation of the sources to run this test suite.
|
||||
|
||||
It is possible to run tests using multiple cores by setting `-jN` in
|
||||
`TEST_PHP_ARGS`:
|
||||
|
||||
make TEST_PHP_ARGS=-j4 test
|
||||
|
||||
Shall run `make test` with a maximum of 4 concurrent jobs: Generally the maximum
|
||||
number of jobs should not exceed the number of cores available.
|
||||
|
||||
The [qa.php.net](https://qa.php.net) site provides more detailed info about
|
||||
testing and quality assurance.
|
||||
|
||||
## Installing PHP built from source
|
||||
|
||||
After a successful build (and test), PHP may be installed with:
|
||||
|
||||
make install
|
||||
|
||||
Depending on your permissions and prefix, `make install` may need super user
|
||||
permissions.
|
||||
|
||||
## PHP extensions
|
||||
|
||||
Extensions provide additional functionality on top of PHP. PHP consists of many
|
||||
essential bundled extensions. Additional extensions can be found in the PHP
|
||||
Extension Community Library - [PECL](https://pecl.php.net).
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are most welcome by forking the
|
||||
[GitHub repository](https://github.com/php/php-src) and sending a pull request.
|
||||
|
||||
Discussions are done on GitHub, but depending on the topic can also be relayed
|
||||
to the official PHP developer mailing list internals@lists.php.net.
|
||||
|
||||
New features require an RFC and must be accepted by the developers. See
|
||||
[Request for comments - RFC](https://wiki.php.net/rfc) and
|
||||
[Voting on PHP features](https://wiki.php.net/rfc/voting) for more information
|
||||
on the process.
|
||||
|
||||
Bug fixes **do not** require an RFC but require a bug tracker ticket. Open a
|
||||
ticket at [bugs.php.net](https://bugs.php.net) and reference the bug id using
|
||||
`#NNNNNN`.
|
||||
Bug fixes **do not** require an RFC, but require a bugtracker ticket. Always
|
||||
open a ticket at http://bugs.php.net and reference the bug id using #NNNNNN.
|
||||
|
||||
Fix #55371: get_magic_quotes_gpc() throws deprecation warning
|
||||
|
||||
After removing magic quotes, the get_magic_quotes_gpc function caused a
|
||||
deprecated warning. get_magic_quotes_gpc can be used to detect the
|
||||
magic_quotes behavior and therefore should not raise a warning at any time.
|
||||
The patch removes this warning.
|
||||
After removing magic quotes, the get_magic_quotes_gpc function caused
|
||||
a deprecate warning. get_magic_quotes_gpc can be used to detected
|
||||
the magic_quotes behavior and therefore should not raise a warning at any
|
||||
time. The patch removes this warning
|
||||
|
||||
See [Git workflow](https://wiki.php.net/vcs/gitworkflow) for more details on the
|
||||
pull request workflow.
|
||||
|
||||
### Guidelines for contributors
|
||||
|
||||
See further documents in the repository for more information on how to
|
||||
contribute:
|
||||
|
||||
- [Contributing to PHP](/CONTRIBUTING.md)
|
||||
- [PHP coding standards](/CODING_STANDARDS.md)
|
||||
- [Mailinglist rules](/docs/mailinglist-rules.md)
|
||||
- [PHP release process](/docs/release-process.md)
|
||||
|
||||
## Credits
|
||||
|
||||
For the list of people who've put work into PHP, please see the
|
||||
[PHP credits page](https://php.net/credits.php).
|
||||
We do not merge pull requests directly on github. All PRs will be
|
||||
pulled and pushed through http://git.php.net.
|
||||
Executable
+174
@@ -0,0 +1,174 @@
|
||||
Design
|
||||
======
|
||||
|
||||
Main assumption of the model is that the problem that we are to solve is the
|
||||
problem of the very long class names in PHP libraries. We would not attempt
|
||||
to take autoloader's job or create packaging model - only make names
|
||||
manageable.
|
||||
|
||||
Namespaces are defined the following way:
|
||||
|
||||
Zend/DB/Connection.php:
|
||||
<?php
|
||||
namespace Zend\DB;
|
||||
|
||||
class Connection {
|
||||
}
|
||||
|
||||
function connect() {
|
||||
}
|
||||
?>
|
||||
|
||||
Namespace definition does the following:
|
||||
All class and function names inside are automatically prefixed with
|
||||
namespace name. Inside namespace, local name always takes precedence over
|
||||
global name. Several files may be using the same namespace.
|
||||
The namespace declaration statement must be the very first statement in
|
||||
the file. The only exception is "declare" statement that can be used before.
|
||||
|
||||
Every class and function in a namespace can be referred to by the full name
|
||||
- e.g. Zend\DB\Connection or Zend\DB\connect - at any time.
|
||||
|
||||
<?php
|
||||
require 'Zend/Db/Connection.php';
|
||||
$x = new Zend\DB\Connection;
|
||||
Zend\DB\connect();
|
||||
?>
|
||||
|
||||
Namespace or class name can be imported:
|
||||
|
||||
<?php
|
||||
require 'Zend/Db/Connection.php';
|
||||
use Zend\DB;
|
||||
use Zend\DB\Connection as DbConnection;
|
||||
|
||||
$x = new Zend\DB\Connection();
|
||||
$y = new DB\connection();
|
||||
$z = new DbConnection();
|
||||
DB\connect();
|
||||
?>
|
||||
|
||||
The use statement only defines name aliasing. It may create name alias for
|
||||
namespace or class. The simple form of statement "use A\B\C\D;" is
|
||||
equivalent to "use A\B\C\D as D;". The use statement can be used at any
|
||||
time in the global scope (not inside function/class) and takes effect from
|
||||
the point of definition down to the end of file. It is recommended however to
|
||||
place the use statements at the beginning of the file. The use statements have
|
||||
effect only on the file where they appear.
|
||||
|
||||
The special "empty" namespace (\ prefix) is useful as explicit global
|
||||
namespace qualification. All class and function names started from \
|
||||
interpreted as global.
|
||||
|
||||
<?php
|
||||
namespace A\B\C;
|
||||
|
||||
$con = \mysql_connect(...);
|
||||
?>
|
||||
|
||||
A special constant __NAMESPACE__ contains the name of the current namespace.
|
||||
It can be used to construct fully-qualified names to pass them as callbacks.
|
||||
|
||||
<?php
|
||||
namespace A\B\C;
|
||||
|
||||
function foo() {
|
||||
}
|
||||
|
||||
set_error_handler(__NAMESPACE__ . "\foo");
|
||||
?>
|
||||
|
||||
In global namespace __NAMESPACE__ constant has the value of empty string.
|
||||
|
||||
Names inside namespace are resolved according to the following rules:
|
||||
|
||||
1) all qualified names are translated during compilation according to
|
||||
current import rules. So if we have "use A\B\C" and then "C\D\e()"
|
||||
it is translated to "A\B\C\D\e()".
|
||||
2) unqualified class names translated during compilation according to
|
||||
current import rules. So if we have "use A\B\C" and then "new C()" it
|
||||
is translated to "new A\B\C()".
|
||||
3) inside namespace, calls to unqualified functions that are defined in
|
||||
current namespace (and are known at the time the call is parsed) are
|
||||
interpreted as calls to these namespace functions.
|
||||
4) inside namespace, calls to unqualified functions that are not defined
|
||||
in current namespace are resolved at run-time. The call to function foo()
|
||||
inside namespace (A\B) first tries to find and call function from current
|
||||
namespace A\B\foo() and if it doesn't exist PHP tries to call internal
|
||||
function foo(). Note that using foo() inside namespace you can call only
|
||||
internal PHP functions, however using \foo() you are able to call any
|
||||
function from the global namespace.
|
||||
5) unqualified class names are resolved at run-time. E.q. "new Exception()"
|
||||
first tries to use (and autoload) class from current namespace and in case
|
||||
of failure uses internal PHP class. Note that using "new A" in namespace
|
||||
you can only create class from this namespace or internal PHP class, however
|
||||
using "new \A" you are able to create any class from the global namespace.
|
||||
6) Calls to qualified functions are resolved at run-time. Call to
|
||||
A\B\foo() first tries to call function foo() from namespace A\B, then
|
||||
it tries to find class A\B (__autoload() it if necessary) and call its
|
||||
static method foo()
|
||||
7) qualified class names are interpreted as class from corresponding
|
||||
namespace. So "new A\B\C()" refers to class C from namespace A\B.
|
||||
|
||||
Examples
|
||||
--------
|
||||
<?php
|
||||
namespace A;
|
||||
foo(); // first tries to call "foo" defined in namespace "A"
|
||||
// then calls internal function "foo"
|
||||
\foo(); // calls function "foo" defined in global scope
|
||||
?>
|
||||
|
||||
<?php
|
||||
namespace A;
|
||||
new B(); // first tries to create object of class "B" defined in namespace "A"
|
||||
// then creates object of internal class "B"
|
||||
new \B(); // creates object of class "B" defined in global scope
|
||||
?>
|
||||
|
||||
<?php
|
||||
namespace A;
|
||||
new A(); // first tries to create object of class "A" from namespace "A" (A\A)
|
||||
// then creates object of internal class "A"
|
||||
?>
|
||||
|
||||
<?php
|
||||
namespace A;
|
||||
B\foo(); // first tries to call function "foo" from namespace "A\B"
|
||||
// then calls method "foo" of internal class "B"
|
||||
\B\foo(); // first tries to call function "foo" from namespace "B"
|
||||
// then calls method "foo" of class "B" from global scope
|
||||
?>
|
||||
|
||||
The worst case if class name conflicts with namespace name
|
||||
<?php
|
||||
namespace A;
|
||||
A\foo(); // first tries to call function "foo" from namespace "A\A"
|
||||
// then tries to call method "foo" of class "A" from namespace "A"
|
||||
// then tries to call function "foo" from namespace "A"
|
||||
// then calls method "foo" of internal class "A"
|
||||
\A\foo(); // first tries to call function "foo" from namespace "A"
|
||||
// then calls method "foo" of class "A" from global scope
|
||||
?>
|
||||
|
||||
TODO
|
||||
====
|
||||
|
||||
* Support for namespace constants?
|
||||
|
||||
* performance problems
|
||||
- calls to internal functions in namespaces are slower, because PHP first
|
||||
looks for such function in current namespace
|
||||
- calls to static methods are slower, because PHP first tries to look
|
||||
for corresponding function in namespace
|
||||
|
||||
* Extend the Reflection API?
|
||||
* Add ReflectionNamespace class
|
||||
+ getName()
|
||||
+ getClasses()
|
||||
+ getFunctions()
|
||||
+ getFiles()
|
||||
* Add getNamespace() methods to ReflectionClass and ReflectionFunction
|
||||
|
||||
* Rename namespaces to packages?
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
## process this file with automake to produce Makefile.am
|
||||
AUTOMAKE_OPTIONS=foreign
|
||||
noinst_LTLIBRARIES=libtsrm.la
|
||||
libtsrm_la_SOURCES = TSRM.c tsrm_strtok_r.c tsrm_virtual_cwd.c
|
||||
|
||||
depend:
|
||||
@@ -0,0 +1,2 @@
|
||||
- Improve the lock in ts_resource_ex() in order to cover less code.
|
||||
This can probably be done by more careful hash table access
|
||||
+334
-323
@@ -6,7 +6,7 @@
|
||||
| This source file is subject to the TSRM license, that is bundled |
|
||||
| with this package in the file LICENSE |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Zeev Suraski <zeev@php.net> |
|
||||
| Authors: Zeev Suraski <zeev@zend.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
@@ -15,20 +15,13 @@
|
||||
#ifdef ZTS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#if ZEND_DEBUG
|
||||
# include <assert.h>
|
||||
# define TSRM_ASSERT(c) assert(c)
|
||||
#else
|
||||
# define TSRM_ASSERT(c)
|
||||
#if HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
typedef struct _tsrm_tls_entry tsrm_tls_entry;
|
||||
|
||||
/* TSRMLS_CACHE_DEFINE; is already done in Zend, this is being always compiled statically. */
|
||||
TSRMLS_CACHE_EXTERN();
|
||||
|
||||
struct _tsrm_tls_entry {
|
||||
void **storage;
|
||||
int count;
|
||||
@@ -41,7 +34,6 @@ typedef struct {
|
||||
size_t size;
|
||||
ts_allocate_ctor ctor;
|
||||
ts_allocate_dtor dtor;
|
||||
size_t fast_offset;
|
||||
int done;
|
||||
} tsrm_resource_type;
|
||||
|
||||
@@ -55,17 +47,12 @@ static ts_rsrc_id id_count;
|
||||
static tsrm_resource_type *resource_types_table=NULL;
|
||||
static int resource_types_table_size;
|
||||
|
||||
/* Reserved space for fast globals access */
|
||||
static size_t tsrm_reserved_pos = 0;
|
||||
static size_t tsrm_reserved_size = 0;
|
||||
|
||||
static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */
|
||||
static MUTEX_T tsrm_env_mutex; /* tsrm environ mutex */
|
||||
static MUTEX_T tsmm_mutex; /* thread-safe memory manager mutex */
|
||||
|
||||
/* New thread handlers */
|
||||
static tsrm_thread_begin_func_t tsrm_new_thread_begin_handler = NULL;
|
||||
static tsrm_thread_end_func_t tsrm_new_thread_end_handler = NULL;
|
||||
static tsrm_shutdown_func_t tsrm_shutdown_handler = NULL;
|
||||
static tsrm_thread_begin_func_t tsrm_new_thread_begin_handler;
|
||||
static tsrm_thread_end_func_t tsrm_new_thread_end_handler;
|
||||
|
||||
/* Debug support */
|
||||
int tsrm_error(int level, const char *format, ...);
|
||||
@@ -74,7 +61,7 @@ int tsrm_error(int level, const char *format, ...);
|
||||
static int tsrm_error_level;
|
||||
static FILE *tsrm_error_file;
|
||||
|
||||
#ifdef TSRM_DEBUG
|
||||
#if TSRM_DEBUG
|
||||
#define TSRM_ERROR(args) tsrm_error args
|
||||
#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \
|
||||
{ \
|
||||
@@ -102,32 +89,49 @@ static FILE *tsrm_error_file;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
static DWORD tls_key;
|
||||
# define tsrm_tls_set(what) TlsSetValue(tls_key, (void*)(what))
|
||||
# define tsrm_tls_get() TlsGetValue(tls_key)
|
||||
#else
|
||||
#if defined(PTHREADS)
|
||||
/* Thread local storage */
|
||||
static pthread_key_t tls_key;
|
||||
# define tsrm_tls_set(what) pthread_setspecific(tls_key, (void*)(what))
|
||||
# define tsrm_tls_get() pthread_getspecific(tls_key)
|
||||
#endif
|
||||
|
||||
TSRM_TLS uint8_t in_main_thread = 0;
|
||||
TSRM_TLS uint8_t is_thread_shutdown = 0;
|
||||
#elif defined(TSRM_ST)
|
||||
static int tls_key;
|
||||
# define tsrm_tls_set(what) st_thread_setspecific(tls_key, (void*)(what))
|
||||
# define tsrm_tls_get() st_thread_getspecific(tls_key)
|
||||
|
||||
#elif defined(TSRM_WIN32)
|
||||
static DWORD tls_key;
|
||||
# define tsrm_tls_set(what) TlsSetValue(tls_key, (void*)(what))
|
||||
# define tsrm_tls_get() TlsGetValue(tls_key)
|
||||
|
||||
#elif defined(BETHREADS)
|
||||
static int32 tls_key;
|
||||
# define tsrm_tls_set(what) tls_set(tls_key, (void*)(what))
|
||||
# define tsrm_tls_get() (tsrm_tls_entry*)tls_get(tls_key)
|
||||
|
||||
#else
|
||||
# define tsrm_tls_set(what)
|
||||
# define tsrm_tls_get() NULL
|
||||
# warning tsrm_set_interpreter_context is probably broken on this platform
|
||||
#endif
|
||||
|
||||
/* Startup TSRM (call once for the entire process) */
|
||||
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, const char *debug_filename)
|
||||
{/*{{{*/
|
||||
#ifdef TSRM_WIN32
|
||||
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename)
|
||||
{
|
||||
#if defined(GNUPTH)
|
||||
pth_init();
|
||||
#elif defined(PTHREADS)
|
||||
pthread_key_create( &tls_key, 0 );
|
||||
#elif defined(TSRM_ST)
|
||||
st_init();
|
||||
st_key_create(&tls_key, 0);
|
||||
#elif defined(TSRM_WIN32)
|
||||
tls_key = TlsAlloc();
|
||||
#else
|
||||
pthread_key_create(&tls_key, 0);
|
||||
#elif defined(BETHREADS)
|
||||
tls_key = tls_allocate();
|
||||
#endif
|
||||
|
||||
/* ensure singleton */
|
||||
in_main_thread = 1;
|
||||
is_thread_shutdown = 0;
|
||||
|
||||
tsrm_error_file = stderr;
|
||||
tsrm_error_set(debug_level, debug_filename);
|
||||
tsrm_tls_table_size = expected_threads;
|
||||
@@ -135,7 +139,6 @@ TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debu
|
||||
tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *));
|
||||
if (!tsrm_tls_table) {
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate TLS table"));
|
||||
is_thread_shutdown = 1;
|
||||
return 0;
|
||||
}
|
||||
id_count=0;
|
||||
@@ -144,132 +147,75 @@ TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debu
|
||||
resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type));
|
||||
if (!resource_types_table) {
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate resource types table"));
|
||||
is_thread_shutdown = 1;
|
||||
free(tsrm_tls_table);
|
||||
tsrm_tls_table = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tsmm_mutex = tsrm_mutex_alloc();
|
||||
|
||||
tsrm_new_thread_begin_handler = tsrm_new_thread_end_handler = NULL;
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Started up TSRM, %d expected threads, %d expected resources", expected_threads, expected_resources));
|
||||
|
||||
tsrm_reserved_pos = 0;
|
||||
tsrm_reserved_size = 0;
|
||||
|
||||
tsrm_env_mutex = tsrm_mutex_alloc();
|
||||
|
||||
return 1;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
/* Shutdown TSRM (call once for the entire process) */
|
||||
TSRM_API void tsrm_shutdown(void)
|
||||
{/*{{{*/
|
||||
{
|
||||
int i;
|
||||
|
||||
if (is_thread_shutdown) {
|
||||
/* shutdown must only occur once */
|
||||
return;
|
||||
}
|
||||
if (tsrm_tls_table) {
|
||||
for (i=0; i<tsrm_tls_table_size; i++) {
|
||||
tsrm_tls_entry *p = tsrm_tls_table[i], *next_p;
|
||||
|
||||
is_thread_shutdown = 1;
|
||||
while (p) {
|
||||
int j;
|
||||
|
||||
if (!in_main_thread) {
|
||||
/* only the main thread may shutdown tsrm */
|
||||
return;
|
||||
}
|
||||
|
||||
for (i=0; i<tsrm_tls_table_size; i++) {
|
||||
tsrm_tls_entry *p = tsrm_tls_table[i], *next_p;
|
||||
|
||||
while (p) {
|
||||
int j;
|
||||
|
||||
next_p = p->next;
|
||||
for (j=0; j<p->count; j++) {
|
||||
if (p->storage[j]) {
|
||||
if (resource_types_table && !resource_types_table[j].done && resource_types_table[j].dtor) {
|
||||
resource_types_table[j].dtor(p->storage[j]);
|
||||
}
|
||||
if (!resource_types_table[j].fast_offset) {
|
||||
next_p = p->next;
|
||||
for (j=0; j<p->count; j++) {
|
||||
if (p->storage[j]) {
|
||||
if (resource_types_table && !resource_types_table[j].done && resource_types_table[j].dtor) {
|
||||
resource_types_table[j].dtor(p->storage[j], &p->storage);
|
||||
}
|
||||
free(p->storage[j]);
|
||||
}
|
||||
}
|
||||
free(p->storage);
|
||||
free(p);
|
||||
p = next_p;
|
||||
}
|
||||
free(p->storage);
|
||||
free(p);
|
||||
p = next_p;
|
||||
}
|
||||
free(tsrm_tls_table);
|
||||
tsrm_tls_table = NULL;
|
||||
}
|
||||
if (resource_types_table) {
|
||||
free(resource_types_table);
|
||||
resource_types_table=NULL;
|
||||
}
|
||||
free(tsrm_tls_table);
|
||||
free(resource_types_table);
|
||||
tsrm_mutex_free(tsmm_mutex);
|
||||
tsrm_mutex_free(tsrm_env_mutex);
|
||||
tsmm_mutex = NULL;
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Shutdown TSRM"));
|
||||
if (tsrm_error_file!=stderr) {
|
||||
fclose(tsrm_error_file);
|
||||
}
|
||||
#ifdef TSRM_WIN32
|
||||
TlsFree(tls_key);
|
||||
#else
|
||||
#if defined(GNUPTH)
|
||||
pth_kill();
|
||||
#elif defined(PTHREADS)
|
||||
pthread_setspecific(tls_key, 0);
|
||||
pthread_key_delete(tls_key);
|
||||
#elif defined(TSRM_WIN32)
|
||||
TlsFree(tls_key);
|
||||
#endif
|
||||
if (tsrm_shutdown_handler) {
|
||||
tsrm_shutdown_handler();
|
||||
}
|
||||
tsrm_new_thread_begin_handler = NULL;
|
||||
tsrm_new_thread_end_handler = NULL;
|
||||
tsrm_shutdown_handler = NULL;
|
||||
|
||||
tsrm_reserved_pos = 0;
|
||||
tsrm_reserved_size = 0;
|
||||
}/*}}}*/
|
||||
|
||||
/* {{{ */
|
||||
/* environ lock api */
|
||||
TSRM_API void tsrm_env_lock(void) {
|
||||
tsrm_mutex_lock(tsrm_env_mutex);
|
||||
}
|
||||
|
||||
TSRM_API void tsrm_env_unlock(void) {
|
||||
tsrm_mutex_unlock(tsrm_env_mutex);
|
||||
} /* }}} */
|
||||
|
||||
/* enlarge the arrays for the already active threads */
|
||||
static void tsrm_update_active_threads(void)
|
||||
{/*{{{*/
|
||||
int i;
|
||||
|
||||
for (i=0; i<tsrm_tls_table_size; i++) {
|
||||
tsrm_tls_entry *p = tsrm_tls_table[i];
|
||||
|
||||
while (p) {
|
||||
if (p->count < id_count) {
|
||||
int j;
|
||||
|
||||
p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
|
||||
for (j=p->count; j<id_count; j++) {
|
||||
if (resource_types_table[j].fast_offset) {
|
||||
p->storage[j] = (void *) (((char*)p) + resource_types_table[j].fast_offset);
|
||||
} else {
|
||||
p->storage[j] = (void *) malloc(resource_types_table[j].size);
|
||||
}
|
||||
if (resource_types_table[j].ctor) {
|
||||
resource_types_table[j].ctor(p->storage[j]);
|
||||
}
|
||||
}
|
||||
p->count = id_count;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* allocates a new thread-safe-resource id */
|
||||
TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
|
||||
{/*{{{*/
|
||||
{
|
||||
int i;
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size));
|
||||
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
@@ -280,140 +226,100 @@ TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate
|
||||
|
||||
/* store the new resource type in the resource sizes table */
|
||||
if (resource_types_table_size < id_count) {
|
||||
tsrm_resource_type *_tmp;
|
||||
_tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
|
||||
if (!_tmp) {
|
||||
resource_types_table = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
|
||||
if (!resource_types_table) {
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
|
||||
*rsrc_id = 0;
|
||||
return 0;
|
||||
}
|
||||
resource_types_table = _tmp;
|
||||
resource_types_table_size = id_count;
|
||||
}
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = 0;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
|
||||
|
||||
tsrm_update_active_threads();
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
/* enlarge the arrays for the already active threads */
|
||||
for (i=0; i<tsrm_tls_table_size; i++) {
|
||||
tsrm_tls_entry *p = tsrm_tls_table[i];
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
|
||||
return *rsrc_id;
|
||||
}/*}}}*/
|
||||
while (p) {
|
||||
if (p->count < id_count) {
|
||||
int j;
|
||||
|
||||
|
||||
/* Reserve space for fast thread-safe-resources */
|
||||
TSRM_API void tsrm_reserve(size_t size)
|
||||
{/*{{{*/
|
||||
tsrm_reserved_pos = 0;
|
||||
tsrm_reserved_size = TSRM_ALIGNED_SIZE(size);
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/* allocates a new fast thread-safe-resource id */
|
||||
TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
|
||||
{/*{{{*/
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new fast resource id, %d bytes", size));
|
||||
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
|
||||
/* obtain a resource id */
|
||||
*rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));
|
||||
|
||||
size = TSRM_ALIGNED_SIZE(size);
|
||||
if (tsrm_reserved_size - tsrm_reserved_pos < size) {
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate space for fast resource"));
|
||||
*rsrc_id = 0;
|
||||
*offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*offset = TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_pos;
|
||||
tsrm_reserved_pos += size;
|
||||
|
||||
/* store the new resource type in the resource sizes table */
|
||||
if (resource_types_table_size < id_count) {
|
||||
tsrm_resource_type *_tmp;
|
||||
_tmp = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);
|
||||
if (!_tmp) {
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
|
||||
*rsrc_id = 0;
|
||||
return 0;
|
||||
p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
|
||||
for (j=p->count; j<id_count; j++) {
|
||||
p->storage[j] = (void *) malloc(resource_types_table[j].size);
|
||||
if (resource_types_table[j].ctor) {
|
||||
resource_types_table[j].ctor(p->storage[j], &p->storage);
|
||||
}
|
||||
}
|
||||
p->count = id_count;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
resource_types_table = _tmp;
|
||||
resource_types_table_size = id_count;
|
||||
}
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].fast_offset = *offset;
|
||||
resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;
|
||||
|
||||
tsrm_update_active_threads();
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
|
||||
return *rsrc_id;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
|
||||
{/*{{{*/
|
||||
{
|
||||
int i;
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Creating data structures for thread %x", thread_id));
|
||||
(*thread_resources_ptr) = (tsrm_tls_entry *) malloc(TSRM_ALIGNED_SIZE(sizeof(tsrm_tls_entry)) + tsrm_reserved_size);
|
||||
(*thread_resources_ptr)->storage = NULL;
|
||||
if (id_count > 0) {
|
||||
(*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
|
||||
}
|
||||
(*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry));
|
||||
(*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
|
||||
(*thread_resources_ptr)->count = id_count;
|
||||
(*thread_resources_ptr)->thread_id = thread_id;
|
||||
(*thread_resources_ptr)->next = NULL;
|
||||
|
||||
/* Set thread local storage to this new thread resources structure */
|
||||
tsrm_tls_set(*thread_resources_ptr);
|
||||
TSRMLS_CACHE = *thread_resources_ptr;
|
||||
|
||||
if (tsrm_new_thread_begin_handler) {
|
||||
tsrm_new_thread_begin_handler(thread_id);
|
||||
tsrm_new_thread_begin_handler(thread_id, &((*thread_resources_ptr)->storage));
|
||||
}
|
||||
for (i=0; i<id_count; i++) {
|
||||
if (resource_types_table[i].done) {
|
||||
(*thread_resources_ptr)->storage[i] = NULL;
|
||||
} else {
|
||||
if (resource_types_table[i].fast_offset) {
|
||||
(*thread_resources_ptr)->storage[i] = (void *) (((char*)(*thread_resources_ptr)) + resource_types_table[i].fast_offset);
|
||||
} else {
|
||||
(*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
|
||||
}
|
||||
} else
|
||||
{
|
||||
(*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
|
||||
if (resource_types_table[i].ctor) {
|
||||
resource_types_table[i].ctor((*thread_resources_ptr)->storage[i]);
|
||||
resource_types_table[i].ctor((*thread_resources_ptr)->storage[i], &(*thread_resources_ptr)->storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tsrm_new_thread_end_handler) {
|
||||
tsrm_new_thread_end_handler(thread_id);
|
||||
tsrm_new_thread_end_handler(thread_id, &((*thread_resources_ptr)->storage));
|
||||
}
|
||||
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
/* fetches the requested resource for the current thread */
|
||||
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
|
||||
{/*{{{*/
|
||||
{
|
||||
THREAD_T thread_id;
|
||||
int hash_value;
|
||||
tsrm_tls_entry *thread_resources;
|
||||
|
||||
#ifdef NETWARE
|
||||
/* The below if loop is added for NetWare to fix an abend while unloading PHP
|
||||
* when an Apache unload command is issued on the system console.
|
||||
* While exiting from PHP, at the end for some reason, this function is called
|
||||
* with tsrm_tls_table = NULL. When this happened, the server abends when
|
||||
* tsrm_tls_table is accessed since it is NULL.
|
||||
*/
|
||||
if(tsrm_tls_table) {
|
||||
#endif
|
||||
if (!th_id) {
|
||||
/* Fast path for looking up the resources for the current
|
||||
* thread. Its used by just about every call to
|
||||
@@ -467,20 +373,80 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id)
|
||||
* changes to the structure as we read it.
|
||||
*/
|
||||
TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
|
||||
}/*}}}*/
|
||||
#ifdef NETWARE
|
||||
} /* if(tsrm_tls_table) */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* frees an interpreter context. You are responsible for making sure that
|
||||
* it is not linked into the TSRM hash, and not marked as the current interpreter */
|
||||
void tsrm_free_interpreter_context(void *context)
|
||||
{
|
||||
tsrm_tls_entry *next, *thread_resources = (tsrm_tls_entry*)context;
|
||||
int i;
|
||||
|
||||
while (thread_resources) {
|
||||
next = thread_resources->next;
|
||||
|
||||
for (i=0; i<thread_resources->count; i++) {
|
||||
if (resource_types_table[i].dtor) {
|
||||
resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage);
|
||||
}
|
||||
}
|
||||
for (i=0; i<thread_resources->count; i++) {
|
||||
free(thread_resources->storage[i]);
|
||||
}
|
||||
free(thread_resources->storage);
|
||||
free(thread_resources);
|
||||
thread_resources = next;
|
||||
}
|
||||
}
|
||||
|
||||
void *tsrm_set_interpreter_context(void *new_ctx)
|
||||
{
|
||||
tsrm_tls_entry *current;
|
||||
|
||||
current = tsrm_tls_get();
|
||||
|
||||
/* TODO: unlink current from the global linked list, and replace it
|
||||
* it with the new context, protected by mutex where/if appropriate */
|
||||
|
||||
/* Set thread local storage to this new thread resources structure */
|
||||
tsrm_tls_set(new_ctx);
|
||||
|
||||
/* return old context, so caller can restore it when they're done */
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
/* allocates a new interpreter context */
|
||||
void *tsrm_new_interpreter_context(void)
|
||||
{
|
||||
tsrm_tls_entry *new_ctx, *current;
|
||||
THREAD_T thread_id;
|
||||
|
||||
thread_id = tsrm_thread_id();
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
|
||||
current = tsrm_tls_get();
|
||||
|
||||
allocate_new_resource(&new_ctx, thread_id);
|
||||
|
||||
/* switch back to the context that was in use prior to our creation
|
||||
* of the new one */
|
||||
return tsrm_set_interpreter_context(current);
|
||||
}
|
||||
|
||||
|
||||
/* frees all resources allocated for the current thread */
|
||||
void ts_free_thread(void)
|
||||
{/*{{{*/
|
||||
{
|
||||
tsrm_tls_entry *thread_resources;
|
||||
int i;
|
||||
THREAD_T thread_id = tsrm_thread_id();
|
||||
int hash_value;
|
||||
tsrm_tls_entry *last=NULL;
|
||||
|
||||
TSRM_ASSERT(!in_main_thread);
|
||||
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
|
||||
thread_resources = tsrm_tls_table[hash_value];
|
||||
@@ -489,13 +455,11 @@ void ts_free_thread(void)
|
||||
if (thread_resources->thread_id == thread_id) {
|
||||
for (i=0; i<thread_resources->count; i++) {
|
||||
if (resource_types_table[i].dtor) {
|
||||
resource_types_table[i].dtor(thread_resources->storage[i]);
|
||||
resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage);
|
||||
}
|
||||
}
|
||||
for (i=0; i<thread_resources->count; i++) {
|
||||
if (!resource_types_table[i].fast_offset) {
|
||||
free(thread_resources->storage[i]);
|
||||
}
|
||||
free(thread_resources->storage[i]);
|
||||
}
|
||||
free(thread_resources->storage);
|
||||
if (last) {
|
||||
@@ -513,11 +477,58 @@ void ts_free_thread(void)
|
||||
thread_resources = thread_resources->next;
|
||||
}
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
/* frees all resources allocated for all threads except current */
|
||||
void ts_free_worker_threads(void)
|
||||
{
|
||||
tsrm_tls_entry *thread_resources;
|
||||
int i;
|
||||
THREAD_T thread_id = tsrm_thread_id();
|
||||
int hash_value;
|
||||
tsrm_tls_entry *last=NULL;
|
||||
|
||||
tsrm_mutex_lock(tsmm_mutex);
|
||||
hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
|
||||
thread_resources = tsrm_tls_table[hash_value];
|
||||
|
||||
while (thread_resources) {
|
||||
if (thread_resources->thread_id != thread_id) {
|
||||
for (i=0; i<thread_resources->count; i++) {
|
||||
if (resource_types_table[i].dtor) {
|
||||
resource_types_table[i].dtor(thread_resources->storage[i], &thread_resources->storage);
|
||||
}
|
||||
}
|
||||
for (i=0; i<thread_resources->count; i++) {
|
||||
free(thread_resources->storage[i]);
|
||||
}
|
||||
free(thread_resources->storage);
|
||||
if (last) {
|
||||
last->next = thread_resources->next;
|
||||
} else {
|
||||
tsrm_tls_table[hash_value] = thread_resources->next;
|
||||
}
|
||||
free(thread_resources);
|
||||
if (last) {
|
||||
thread_resources = last->next;
|
||||
} else {
|
||||
thread_resources = tsrm_tls_table[hash_value];
|
||||
}
|
||||
} else {
|
||||
if (thread_resources->next) {
|
||||
last = thread_resources;
|
||||
}
|
||||
thread_resources = thread_resources->next;
|
||||
}
|
||||
}
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
}
|
||||
|
||||
|
||||
/* deallocates all occurrences of a given id */
|
||||
void ts_free_id(ts_rsrc_id id)
|
||||
{/*{{{*/
|
||||
{
|
||||
int i;
|
||||
int j = TSRM_UNSHUFFLE_RSRC_ID(id);
|
||||
|
||||
@@ -532,11 +543,9 @@ void ts_free_id(ts_rsrc_id id)
|
||||
while (p) {
|
||||
if (p->count > j && p->storage[j]) {
|
||||
if (resource_types_table && resource_types_table[j].dtor) {
|
||||
resource_types_table[j].dtor(p->storage[j]);
|
||||
}
|
||||
if (!resource_types_table[j].fast_offset) {
|
||||
free(p->storage[j]);
|
||||
resource_types_table[j].dtor(p->storage[j], &p->storage);
|
||||
}
|
||||
free(p->storage[j]);
|
||||
p->storage[j] = NULL;
|
||||
}
|
||||
p = p->next;
|
||||
@@ -548,7 +557,9 @@ void ts_free_id(ts_rsrc_id id)
|
||||
tsrm_mutex_unlock(tsmm_mutex);
|
||||
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully freed resource id %d", id));
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@@ -557,49 +568,83 @@ void ts_free_id(ts_rsrc_id id)
|
||||
|
||||
/* Obtain the current thread id */
|
||||
TSRM_API THREAD_T tsrm_thread_id(void)
|
||||
{/*{{{*/
|
||||
{
|
||||
#ifdef TSRM_WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
#elif defined(GNUPTH)
|
||||
return pth_self();
|
||||
#elif defined(PTHREADS)
|
||||
return pthread_self();
|
||||
#elif defined(NSAPI)
|
||||
return systhread_current();
|
||||
#elif defined(PI3WEB)
|
||||
return PIThread_getCurrent();
|
||||
#elif defined(TSRM_ST)
|
||||
return st_thread_self();
|
||||
#elif defined(BETHREADS)
|
||||
return find_thread(NULL);
|
||||
#endif
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
/* Allocate a mutex */
|
||||
TSRM_API MUTEX_T tsrm_mutex_alloc(void)
|
||||
{/*{{{*/
|
||||
{
|
||||
MUTEX_T mutexp;
|
||||
#ifdef TSRM_WIN32
|
||||
mutexp = malloc(sizeof(CRITICAL_SECTION));
|
||||
InitializeCriticalSection(mutexp);
|
||||
#else
|
||||
#elif defined(GNUPTH)
|
||||
mutexp = (MUTEX_T) malloc(sizeof(*mutexp));
|
||||
pth_mutex_init(mutexp);
|
||||
#elif defined(PTHREADS)
|
||||
mutexp = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init(mutexp,NULL);
|
||||
#elif defined(NSAPI)
|
||||
mutexp = crit_init();
|
||||
#elif defined(PI3WEB)
|
||||
mutexp = PIPlatform_allocLocalMutex();
|
||||
#elif defined(TSRM_ST)
|
||||
mutexp = st_mutex_new();
|
||||
#elif defined(BETHREADS)
|
||||
mutexp = (beos_ben*)malloc(sizeof(beos_ben));
|
||||
mutexp->ben = 0;
|
||||
mutexp->sem = create_sem(1, "PHP sempahore");
|
||||
#endif
|
||||
#ifdef THR_DEBUG
|
||||
printf("Mutex created thread: %d\n",mythreadid());
|
||||
#endif
|
||||
return( mutexp );
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
/* Free a mutex */
|
||||
TSRM_API void tsrm_mutex_free(MUTEX_T mutexp)
|
||||
{/*{{{*/
|
||||
{
|
||||
if (mutexp) {
|
||||
#ifdef TSRM_WIN32
|
||||
DeleteCriticalSection(mutexp);
|
||||
free(mutexp);
|
||||
#else
|
||||
#elif defined(GNUPTH)
|
||||
free(mutexp);
|
||||
#elif defined(PTHREADS)
|
||||
pthread_mutex_destroy(mutexp);
|
||||
free(mutexp);
|
||||
#elif defined(NSAPI)
|
||||
crit_terminate(mutexp);
|
||||
#elif defined(PI3WEB)
|
||||
PISync_delete(mutexp);
|
||||
#elif defined(TSRM_ST)
|
||||
st_mutex_destroy(mutexp);
|
||||
#elif defined(BETHREADS)
|
||||
delete_sem(mutexp->sem);
|
||||
free(mutexp);
|
||||
#endif
|
||||
}
|
||||
#ifdef THR_DEBUG
|
||||
printf("Mutex freed thread: %d\n",mythreadid());
|
||||
#endif
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@@ -607,15 +652,31 @@ TSRM_API void tsrm_mutex_free(MUTEX_T mutexp)
|
||||
A return value of 0 indicates success
|
||||
*/
|
||||
TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp)
|
||||
{/*{{{*/
|
||||
{
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex locked thread: %ld", tsrm_thread_id()));
|
||||
#ifdef TSRM_WIN32
|
||||
EnterCriticalSection(mutexp);
|
||||
return 0;
|
||||
#else
|
||||
#elif defined(GNUPTH)
|
||||
if (pth_mutex_acquire(mutexp, 0, NULL)) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
#elif defined(PTHREADS)
|
||||
return pthread_mutex_lock(mutexp);
|
||||
#elif defined(NSAPI)
|
||||
crit_enter(mutexp);
|
||||
return 0;
|
||||
#elif defined(PI3WEB)
|
||||
return PISync_lock(mutexp);
|
||||
#elif defined(TSRM_ST)
|
||||
return st_mutex_lock(mutexp);
|
||||
#elif defined(BETHREADS)
|
||||
if (atomic_add(&mutexp->ben, 1) != 0)
|
||||
return acquire_sem(mutexp->sem);
|
||||
return 0;
|
||||
#endif
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@@ -623,63 +684,75 @@ TSRM_API int tsrm_mutex_lock(MUTEX_T mutexp)
|
||||
A return value of 0 indicates success
|
||||
*/
|
||||
TSRM_API int tsrm_mutex_unlock(MUTEX_T mutexp)
|
||||
{/*{{{*/
|
||||
{
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Mutex unlocked thread: %ld", tsrm_thread_id()));
|
||||
#ifdef TSRM_WIN32
|
||||
LeaveCriticalSection(mutexp);
|
||||
return 0;
|
||||
#else
|
||||
#elif defined(GNUPTH)
|
||||
if (pth_mutex_release(mutexp)) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
#elif defined(PTHREADS)
|
||||
return pthread_mutex_unlock(mutexp);
|
||||
#elif defined(NSAPI)
|
||||
crit_exit(mutexp);
|
||||
return 0;
|
||||
#elif defined(PI3WEB)
|
||||
return PISync_unlock(mutexp);
|
||||
#elif defined(TSRM_ST)
|
||||
return st_mutex_unlock(mutexp);
|
||||
#elif defined(BETHREADS)
|
||||
if (atomic_add(&mutexp->ben, -1) != 1)
|
||||
return release_sem(mutexp->sem);
|
||||
return 0;
|
||||
#endif
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
/*
|
||||
Changes the signal mask of the calling thread
|
||||
*/
|
||||
#ifdef HAVE_SIGPROCMASK
|
||||
TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset)
|
||||
{/*{{{*/
|
||||
{
|
||||
TSRM_ERROR((TSRM_ERROR_LEVEL_INFO, "Changed sigmask in thread: %ld", tsrm_thread_id()));
|
||||
|
||||
return pthread_sigmask(how, set, oldset);
|
||||
}/*}}}*/
|
||||
/* TODO: add support for other APIs */
|
||||
#ifdef PTHREADS
|
||||
return pthread_sigmask(how, set, oldset);
|
||||
#else
|
||||
return sigprocmask(how, set, oldset);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler)
|
||||
{/*{{{*/
|
||||
{
|
||||
void *retval = (void *) tsrm_new_thread_begin_handler;
|
||||
|
||||
tsrm_new_thread_begin_handler = new_thread_begin_handler;
|
||||
return retval;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler)
|
||||
{/*{{{*/
|
||||
{
|
||||
void *retval = (void *) tsrm_new_thread_end_handler;
|
||||
|
||||
tsrm_new_thread_end_handler = new_thread_end_handler;
|
||||
return retval;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler)
|
||||
{/*{{{*/
|
||||
void *retval = (void *) tsrm_shutdown_handler;
|
||||
|
||||
tsrm_shutdown_handler = shutdown_handler;
|
||||
return retval;
|
||||
}/*}}}*/
|
||||
|
||||
|
||||
/*
|
||||
* Debug support
|
||||
*/
|
||||
|
||||
#ifdef TSRM_DEBUG
|
||||
#if TSRM_DEBUG
|
||||
int tsrm_error(int level, const char *format, ...)
|
||||
{/*{{{*/
|
||||
{
|
||||
if (level<=tsrm_error_level) {
|
||||
va_list args;
|
||||
int size;
|
||||
@@ -694,15 +767,15 @@ int tsrm_error(int level, const char *format, ...)
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}/*}}}*/
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void tsrm_error_set(int level, const char *debug_filename)
|
||||
{/*{{{*/
|
||||
void tsrm_error_set(int level, char *debug_filename)
|
||||
{
|
||||
tsrm_error_level = level;
|
||||
|
||||
#ifdef TSRM_DEBUG
|
||||
#if TSRM_DEBUG
|
||||
if (tsrm_error_file!=stderr) { /* close files opened earlier */
|
||||
fclose(tsrm_error_file);
|
||||
}
|
||||
@@ -716,68 +789,6 @@ void tsrm_error_set(int level, const char *debug_filename)
|
||||
tsrm_error_file = stderr;
|
||||
}
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API void *tsrm_get_ls_cache(void)
|
||||
{/*{{{*/
|
||||
return tsrm_tls_get();
|
||||
}/*}}}*/
|
||||
|
||||
/* Returns offset of tsrm_ls_cache slot from Thread Control Block address */
|
||||
TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void)
|
||||
{/*{{{*/
|
||||
#if defined(__APPLE__) && defined(__x86_64__)
|
||||
// TODO: Implement support for fast JIT ZTS code ???
|
||||
return 0;
|
||||
#elif defined(__x86_64__) && defined(__GNUC__) && !defined(__FreeBSD__)
|
||||
size_t ret;
|
||||
|
||||
asm ("movq _tsrm_ls_cache@gottpoff(%%rip),%0"
|
||||
: "=r" (ret));
|
||||
return ret;
|
||||
#elif defined(__i386__) && defined(__GNUC__) && !defined(__FreeBSD__)
|
||||
size_t ret;
|
||||
|
||||
asm ("leal _tsrm_ls_cache@ntpoff,%0"
|
||||
: "=r" (ret));
|
||||
return ret;
|
||||
#elif defined(__aarch64__)
|
||||
size_t ret;
|
||||
|
||||
# ifdef __APPLE__
|
||||
// Points to struct TLVDecriptor for _tsrm_ls_cache in macOS.
|
||||
asm("adrp %0, #__tsrm_ls_cache@TLVPPAGE\n\t"
|
||||
"ldr %0, [%0, #__tsrm_ls_cache@TLVPPAGEOFF]"
|
||||
: "=r" (ret));
|
||||
# else
|
||||
asm("mov %0, xzr\n\t"
|
||||
"add %0, %0, #:tprel_hi12:_tsrm_ls_cache, lsl #12\n\t"
|
||||
"add %0, %0, #:tprel_lo12_nc:_tsrm_ls_cache"
|
||||
: "=r" (ret));
|
||||
# endif
|
||||
return ret;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API uint8_t tsrm_is_main_thread(void)
|
||||
{/*{{{*/
|
||||
return in_main_thread;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API uint8_t tsrm_is_shutdown(void)
|
||||
{/*{{{*/
|
||||
return is_thread_shutdown;
|
||||
}/*}}}*/
|
||||
|
||||
TSRM_API const char *tsrm_api_name(void)
|
||||
{/*{{{*/
|
||||
#ifdef TSRM_WIN32
|
||||
return "Windows Threads";
|
||||
#else
|
||||
return "POSIX Threads";
|
||||
#endif
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
#endif /* ZTS */
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
# Microsoft Developer Studio Project File - Name="TSRM" - Package Owner=<4>
|
||||
# Microsoft Developer Studio Generated Build File, Format Version 6.00
|
||||
# ** DO NOT EDIT **
|
||||
|
||||
# TARGTYPE "Win32 (x86) Static Library" 0x0104
|
||||
|
||||
CFG=TSRM - Win32 Debug_TS
|
||||
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
|
||||
!MESSAGE use the Export Makefile command and run
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "TSRM.mak".
|
||||
!MESSAGE
|
||||
!MESSAGE You can specify a configuration when running NMAKE
|
||||
!MESSAGE by defining the macro CFG on the command line. For example:
|
||||
!MESSAGE
|
||||
!MESSAGE NMAKE /f "TSRM.mak" CFG="TSRM - Win32 Debug_TS"
|
||||
!MESSAGE
|
||||
!MESSAGE Possible choices for configuration are:
|
||||
!MESSAGE
|
||||
!MESSAGE "TSRM - Win32 Debug_TS" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "TSRM - Win32 Release_TS" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "TSRM - Win32 Release_TS_inline" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE "TSRM - Win32 Release_TSDbg" (based on "Win32 (x86) Static Library")
|
||||
!MESSAGE
|
||||
|
||||
# Begin Project
|
||||
# PROP AllowPerConfigDependencies 0
|
||||
# PROP Scc_ProjName ""
|
||||
# PROP Scc_LocalPath ""
|
||||
CPP=cl.exe
|
||||
RSC=rc.exe
|
||||
|
||||
!IF "$(CFG)" == "TSRM - Win32 Debug_TS"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 1
|
||||
# PROP BASE Output_Dir "TSRM___Win32_Debug_TS"
|
||||
# PROP BASE Intermediate_Dir "TSRM___Win32_Debug_TS"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 1
|
||||
# PROP Output_Dir "Debug_TS"
|
||||
# PROP Intermediate_Dir "Debug_TS"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "C:\Projects\TSRM" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /D "_DEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=1 /YX /FD /GZ /c
|
||||
# ADD BASE RSC /l 0x40d /d "_DEBUG"
|
||||
# ADD RSC /l 0x40d /d "_DEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ELSEIF "$(CFG)" == "TSRM - Win32 Release_TS"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "TSRM___Win32_Release_TS"
|
||||
# PROP BASE Intermediate_Dir "TSRM___Win32_Release_TS"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release_TS"
|
||||
# PROP Intermediate_Dir "Release_TS"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=0 /YX /FD /c
|
||||
# ADD BASE RSC /l 0x40d /d "NDEBUG"
|
||||
# ADD RSC /l 0x40d /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ELSEIF "$(CFG)" == "TSRM - Win32 Release_TS_inline"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "TSRM___Win32_Release_TS_inline"
|
||||
# PROP BASE Intermediate_Dir "TSRM___Win32_Release_TS_inline"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release_TS_inline"
|
||||
# PROP Intermediate_Dir "Release_TS_inline"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "TSRM_EXPORTS" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=0 /YX /FD /c
|
||||
# ADD BASE RSC /l 0x40d /d "NDEBUG"
|
||||
# ADD RSC /l 0x40d /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ELSEIF "$(CFG)" == "TSRM - Win32 Release_TSDbg"
|
||||
|
||||
# PROP BASE Use_MFC 0
|
||||
# PROP BASE Use_Debug_Libraries 0
|
||||
# PROP BASE Output_Dir "TSRM___Win32_Release_TSDbg"
|
||||
# PROP BASE Intermediate_Dir "TSRM___Win32_Release_TSDbg"
|
||||
# PROP BASE Target_Dir ""
|
||||
# PROP Use_MFC 0
|
||||
# PROP Use_Debug_Libraries 0
|
||||
# PROP Output_Dir "Release_TSDbg"
|
||||
# PROP Intermediate_Dir "Release_TSDbg"
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=0 /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "." /D "NDEBUG" /D "ZTS" /D "_LIB" /D "TSRM_EXPORTS" /D "WIN32" /D "_MBCS" /D TSRM_DEBUG=0 /YX /FD /c
|
||||
# ADD BASE RSC /l 0x40d /d "NDEBUG"
|
||||
# ADD RSC /l 0x40d /d "NDEBUG"
|
||||
BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LIB32=link.exe -lib
|
||||
# ADD BASE LIB32 /nologo
|
||||
# ADD LIB32 /nologo
|
||||
|
||||
!ENDIF
|
||||
|
||||
# Begin Target
|
||||
|
||||
# Name "TSRM - Win32 Debug_TS"
|
||||
# Name "TSRM - Win32 Release_TS"
|
||||
# Name "TSRM - Win32 Release_TS_inline"
|
||||
# Name "TSRM - Win32 Release_TSDbg"
|
||||
# Begin Group "Source Files"
|
||||
|
||||
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\TSRM.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tsrm_strtok_r.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tsrm_virtual_cwd.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tsrm_win32.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "Header Files"
|
||||
|
||||
# PROP Default_Filter "h;hpp;hxx;hm;inl"
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\readdir.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\TSRM.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tsrm_config.w32.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tsrm_config_common.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tsrm_strtok_r.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tsrm_virtual_cwd.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tsrm_win32.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
+71
-79
@@ -6,7 +6,7 @@
|
||||
| This source file is subject to the TSRM license, that is bundled |
|
||||
| with this package in the file LICENSE |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Zeev Suraski <zeev@php.net> |
|
||||
| Authors: Zeev Suraski <zeev@zend.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
|
||||
#if !defined(__CYGWIN__) && defined(WIN32)
|
||||
# define TSRM_WIN32
|
||||
# include "Zend/zend_config.w32.h"
|
||||
# include "tsrm_config.w32.h"
|
||||
#else
|
||||
# include "main/php_config.h"
|
||||
# include <tsrm_config.h>
|
||||
#endif
|
||||
|
||||
#include "main/php_stdint.h"
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
# ifdef TSRM_EXPORTS
|
||||
# define TSRM_API __declspec(dllexport)
|
||||
@@ -34,8 +32,13 @@
|
||||
# define TSRM_API
|
||||
#endif
|
||||
|
||||
typedef intptr_t tsrm_intptr_t;
|
||||
typedef uintptr_t tsrm_uintptr_t;
|
||||
#ifdef _WIN64
|
||||
typedef __int64 tsrm_intptr_t;
|
||||
typedef unsigned __int64 tsrm_uintptr_t;
|
||||
#else
|
||||
typedef long tsrm_intptr_t;
|
||||
typedef unsigned long tsrm_uintptr_t;
|
||||
#endif
|
||||
|
||||
/* Only compile multi-threading functions if we're in ZTS mode */
|
||||
#ifdef ZTS
|
||||
@@ -44,16 +47,17 @@ typedef uintptr_t tsrm_uintptr_t;
|
||||
# ifndef TSRM_INCLUDE_FULL_WINDOWS_HEADERS
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
#else
|
||||
# include <windows.h>
|
||||
# include <shellapi.h>
|
||||
#elif defined(GNUPTH)
|
||||
# include <pth.h>
|
||||
#elif defined(PTHREADS)
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
#if SIZEOF_SIZE_T == 4
|
||||
# define TSRM_ALIGNED_SIZE(size) \
|
||||
(((size) + INT32_C(15)) & ~INT32_C(15))
|
||||
#else
|
||||
# define TSRM_ALIGNED_SIZE(size) \
|
||||
(((size) + INT64_C(15)) & ~INT64_C(15))
|
||||
#elif defined(TSRM_ST)
|
||||
# include <st.h>
|
||||
#elif defined(BETHREADS)
|
||||
#include <kernel/OS.h>
|
||||
#include <TLS.h>
|
||||
#endif
|
||||
|
||||
typedef int ts_rsrc_id;
|
||||
@@ -62,15 +66,36 @@ typedef int ts_rsrc_id;
|
||||
#ifdef TSRM_WIN32
|
||||
# define THREAD_T DWORD
|
||||
# define MUTEX_T CRITICAL_SECTION *
|
||||
#else
|
||||
#elif defined(GNUPTH)
|
||||
# define THREAD_T pth_t
|
||||
# define MUTEX_T pth_mutex_t *
|
||||
#elif defined(PTHREADS)
|
||||
# define THREAD_T pthread_t
|
||||
# define MUTEX_T pthread_mutex_t *
|
||||
#elif defined(NSAPI)
|
||||
# define THREAD_T SYS_THREAD
|
||||
# define MUTEX_T CRITICAL
|
||||
#elif defined(PI3WEB)
|
||||
# define THREAD_T PIThread *
|
||||
# define MUTEX_T PISync *
|
||||
#elif defined(TSRM_ST)
|
||||
# define THREAD_T st_thread_t
|
||||
# define MUTEX_T st_mutex_t
|
||||
#elif defined(BETHREADS)
|
||||
# define THREAD_T thread_id
|
||||
typedef struct {
|
||||
sem_id sem;
|
||||
int32 ben;
|
||||
} beos_ben;
|
||||
# define MUTEX_T beos_ben *
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
typedef void (*ts_allocate_ctor)(void *);
|
||||
typedef void (*ts_allocate_dtor)(void *);
|
||||
typedef void (*ts_allocate_ctor)(void *, void ***);
|
||||
typedef void (*ts_allocate_dtor)(void *, void ***);
|
||||
|
||||
#define THREAD_HASH_OF(thr,ts) (unsigned long)thr%(unsigned long)ts
|
||||
|
||||
@@ -79,20 +104,12 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* startup/shutdown */
|
||||
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, const char *debug_filename);
|
||||
TSRM_API int tsrm_startup(int expected_threads, int expected_resources, int debug_level, char *debug_filename);
|
||||
TSRM_API void tsrm_shutdown(void);
|
||||
|
||||
/* environ lock API */
|
||||
TSRM_API void tsrm_env_lock(void);
|
||||
TSRM_API void tsrm_env_unlock(void);
|
||||
|
||||
/* allocates a new thread-safe-resource id */
|
||||
TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor);
|
||||
|
||||
/* Fast resource in reserved (pre-allocated) space */
|
||||
TSRM_API void tsrm_reserve(size_t size);
|
||||
TSRM_API ts_rsrc_id ts_allocate_fast_id(ts_rsrc_id *rsrc_id, size_t *offset, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor);
|
||||
|
||||
/* fetches the requested resource for the current thread */
|
||||
TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id);
|
||||
#define ts_resource(id) ts_resource_ex(id, NULL)
|
||||
@@ -100,6 +117,9 @@ TSRM_API void *ts_resource_ex(ts_rsrc_id id, THREAD_T *th_id);
|
||||
/* frees all resources allocated for the current thread */
|
||||
TSRM_API void ts_free_thread(void);
|
||||
|
||||
/* frees all resources allocated for all threads except current */
|
||||
void ts_free_worker_threads(void);
|
||||
|
||||
/* deallocates all occurrences of a given id */
|
||||
TSRM_API void ts_free_id(ts_rsrc_id id);
|
||||
|
||||
@@ -109,13 +129,12 @@ TSRM_API void ts_free_id(ts_rsrc_id id);
|
||||
#define TSRM_ERROR_LEVEL_CORE 2
|
||||
#define TSRM_ERROR_LEVEL_INFO 3
|
||||
|
||||
typedef void (*tsrm_thread_begin_func_t)(THREAD_T thread_id);
|
||||
typedef void (*tsrm_thread_end_func_t)(THREAD_T thread_id);
|
||||
typedef void (*tsrm_shutdown_func_t)(void);
|
||||
typedef void (*tsrm_thread_begin_func_t)(THREAD_T thread_id, void ***tsrm_ls);
|
||||
typedef void (*tsrm_thread_end_func_t)(THREAD_T thread_id, void ***tsrm_ls);
|
||||
|
||||
|
||||
TSRM_API int tsrm_error(int level, const char *format, ...);
|
||||
TSRM_API void tsrm_error_set(int level, const char *debug_filename);
|
||||
TSRM_API void tsrm_error_set(int level, char *debug_filename);
|
||||
|
||||
/* utility functions */
|
||||
TSRM_API THREAD_T tsrm_thread_id(void);
|
||||
@@ -129,48 +148,24 @@ TSRM_API int tsrm_sigmask(int how, const sigset_t *set, sigset_t *oldset);
|
||||
|
||||
TSRM_API void *tsrm_set_new_thread_begin_handler(tsrm_thread_begin_func_t new_thread_begin_handler);
|
||||
TSRM_API void *tsrm_set_new_thread_end_handler(tsrm_thread_end_func_t new_thread_end_handler);
|
||||
TSRM_API void *tsrm_set_shutdown_handler(tsrm_shutdown_func_t shutdown_handler);
|
||||
|
||||
TSRM_API void *tsrm_get_ls_cache(void);
|
||||
TSRM_API size_t tsrm_get_ls_cache_tcb_offset(void);
|
||||
TSRM_API uint8_t tsrm_is_main_thread(void);
|
||||
TSRM_API uint8_t tsrm_is_shutdown(void);
|
||||
TSRM_API const char *tsrm_api_name(void);
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
# define TSRM_TLS __declspec(thread)
|
||||
#else
|
||||
# define TSRM_TLS __thread
|
||||
#endif
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0
|
||||
#endif
|
||||
|
||||
#if !__has_attribute(tls_model) || defined(__FreeBSD__) || defined(__MUSL__)
|
||||
# define TSRM_TLS_MODEL_ATTR
|
||||
#elif __PIC__
|
||||
# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("initial-exec")))
|
||||
#else
|
||||
# define TSRM_TLS_MODEL_ATTR __attribute__((tls_model("local-exec")))
|
||||
#endif
|
||||
/* these 3 APIs should only be used by people that fully understand the threading model
|
||||
* used by PHP/Zend and the selected SAPI. */
|
||||
TSRM_API void *tsrm_new_interpreter_context(void);
|
||||
TSRM_API void *tsrm_set_interpreter_context(void *new_ctx);
|
||||
TSRM_API void tsrm_free_interpreter_context(void *context);
|
||||
|
||||
#define TSRM_SHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)+1)
|
||||
#define TSRM_UNSHUFFLE_RSRC_ID(rsrc_id) ((rsrc_id)-1)
|
||||
|
||||
#define TSRMG(id, type, element) (TSRMG_BULK(id, type)->element)
|
||||
#define TSRMG_BULK(id, type) ((type) (*((void ***) tsrm_get_ls_cache()))[TSRM_UNSHUFFLE_RSRC_ID(id)])
|
||||
#define TSRMG_FAST(offset, type, element) (TSRMG_FAST_BULK(offset, type)->element)
|
||||
#define TSRMG_FAST_BULK(offset, type) ((type) (((char*) tsrm_get_ls_cache())+(offset)))
|
||||
|
||||
#define TSRMG_STATIC(id, type, element) (TSRMG_BULK_STATIC(id, type)->element)
|
||||
#define TSRMG_BULK_STATIC(id, type) ((type) (*((void ***) TSRMLS_CACHE))[TSRM_UNSHUFFLE_RSRC_ID(id)])
|
||||
#define TSRMG_FAST_STATIC(offset, type, element) (TSRMG_FAST_BULK_STATIC(offset, type)->element)
|
||||
#define TSRMG_FAST_BULK_STATIC(offset, type) ((type) (((char*) TSRMLS_CACHE)+(offset)))
|
||||
#define TSRMLS_CACHE_EXTERN() extern TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR;
|
||||
#define TSRMLS_CACHE_DEFINE() TSRM_TLS void *TSRMLS_CACHE TSRM_TLS_MODEL_ATTR = NULL;
|
||||
#define TSRMLS_CACHE_UPDATE() TSRMLS_CACHE = tsrm_get_ls_cache()
|
||||
#define TSRMLS_CACHE _tsrm_ls_cache
|
||||
#define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
|
||||
#define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = (void ***) ctx
|
||||
#define TSRMLS_SET_CTX(ctx) ctx = (void ***) tsrm_ls
|
||||
#define TSRMG(id, type, element) (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
|
||||
#define TSRMLS_D void ***tsrm_ls
|
||||
#define TSRMLS_DC , TSRMLS_D
|
||||
#define TSRMLS_C tsrm_ls
|
||||
#define TSRMLS_CC , TSRMLS_C
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
@@ -178,16 +173,13 @@ TSRM_API const char *tsrm_api_name(void);
|
||||
|
||||
#else /* non ZTS */
|
||||
|
||||
#define tsrm_env_lock()
|
||||
#define tsrm_env_unlock()
|
||||
|
||||
#define TSRMG_STATIC(id, type, element)
|
||||
#define TSRMLS_CACHE_EXTERN()
|
||||
#define TSRMLS_CACHE_DEFINE()
|
||||
#define TSRMLS_CACHE_UPDATE()
|
||||
#define TSRMLS_CACHE
|
||||
|
||||
#define TSRM_TLS
|
||||
#define TSRMLS_FETCH()
|
||||
#define TSRMLS_FETCH_FROM_CTX(ctx)
|
||||
#define TSRMLS_SET_CTX(ctx)
|
||||
#define TSRMLS_D void
|
||||
#define TSRMLS_DC
|
||||
#define TSRMLS_C
|
||||
#define TSRMLS_CC
|
||||
|
||||
#endif /* ZTS */
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
AC_DEFUN([AM_SET_LIBTOOL_VARIABLE],[
|
||||
LIBTOOL='$(SHELL) $(top_builddir)/libtool $1'
|
||||
])
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Makefile to generate build tools
|
||||
#
|
||||
# Standard usage:
|
||||
# make -f build.mk
|
||||
#
|
||||
# Written by Sascha Schumann
|
||||
#
|
||||
# $Id$
|
||||
|
||||
|
||||
LT_TARGETS = ltmain.sh ltconfig
|
||||
|
||||
config_h_in = tsrm_config.h.in
|
||||
|
||||
makefile_am_files = Makefile.am
|
||||
makefile_in_files = $(makefile_am_files:.am=.in)
|
||||
makefile_files = $(makefile_am_files:e.am=e)
|
||||
|
||||
targets = $(makefile_in_files) $(LT_TARGETS) configure $(config_h_in)
|
||||
|
||||
all: $(targets)
|
||||
|
||||
clean:
|
||||
rm -f $(targets)
|
||||
|
||||
$(LT_TARGETS):
|
||||
rm -f $(LT_TARGETS)
|
||||
libtoolize --automake $(AMFLAGS) -f
|
||||
|
||||
$(makefile_in_files): $(makefile_am_files)
|
||||
automake -a -i $(AMFLAGS) $(makefile_files)
|
||||
|
||||
aclocal.m4: configure.in acinclude.m4
|
||||
aclocal
|
||||
|
||||
$(config_h_in): configure.in
|
||||
# explicitly remove target since autoheader does not seem to work
|
||||
# correctly otherwise (timestamps are not updated)
|
||||
@rm -f $@
|
||||
autoheader
|
||||
|
||||
configure: aclocal.m4 configure.in
|
||||
autoconf
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
|
||||
case "$1" in
|
||||
--copy)
|
||||
automake_flags=--copy
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
|
||||
libtoolize --force --automake $automake_flags
|
||||
|
||||
mv aclocal.m4 aclocal.m4.old 2>/dev/null
|
||||
aclocal
|
||||
if cmp aclocal.m4.old aclocal.m4 > /dev/null 2>&1; then
|
||||
echo "buildconf: keeping ${1}aclocal.m4"
|
||||
mv aclocal.m4.old aclocal.m4
|
||||
else
|
||||
echo "buildconf: created or modified ${1}aclocal.m4"
|
||||
fi
|
||||
|
||||
autoheader
|
||||
|
||||
automake --add-missing --include-deps $automake_flags
|
||||
|
||||
mv configure configure.old 2>/dev/null
|
||||
autoconf
|
||||
if cmp configure.old configure > /dev/null 2>&1; then
|
||||
echo "buildconf: keeping ${1}configure"
|
||||
mv configure.old configure
|
||||
else
|
||||
echo "buildconf: created or modified ${1}configure"
|
||||
fi
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
// vim:ft=javascript
|
||||
// $Id$
|
||||
|
||||
ADD_SOURCES("TSRM", "TSRM.c tsrm_strtok_r.c tsrm_virtual_cwd.c tsrm_win32.c");
|
||||
|
||||
ADD_SOURCES("TSRM", "TSRM.c tsrm_win32.c");
|
||||
ADD_FLAG("CFLAGS_BD_TSRM", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
|
||||
@@ -0,0 +1,35 @@
|
||||
dnl $Id$
|
||||
dnl
|
||||
dnl Minimalistic configure.in for TSRM.
|
||||
dnl
|
||||
|
||||
AC_INIT(TSRM.c)
|
||||
AM_INIT_AUTOMAKE(TSRM, 1.0, nodefine)
|
||||
AM_CONFIG_HEADER(tsrm_config.h)
|
||||
|
||||
AH_TOP([
|
||||
#undef PTHREADS
|
||||
])
|
||||
|
||||
sinclude(tsrm.m4)
|
||||
|
||||
TSRM_BASIC_CHECKS
|
||||
TSRM_THREADS_CHECKS
|
||||
|
||||
AM_PROG_LIBTOOL
|
||||
if test "$enable_debug" != "yes"; then
|
||||
AM_SET_LIBTOOL_VARIABLE([--silent])
|
||||
fi
|
||||
|
||||
dnl TSRM_PTHREAD
|
||||
|
||||
AC_CHECK_HEADERS(
|
||||
utime.h \
|
||||
dirent.h \
|
||||
stdarg.h \
|
||||
alloca.h \
|
||||
unistd.h \
|
||||
limits.h
|
||||
)
|
||||
|
||||
AC_OUTPUT(Makefile)
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef READDIR_H
|
||||
#define READDIR_H
|
||||
|
||||
|
||||
/*
|
||||
* Structures and types used to implement opendir/readdir/closedir
|
||||
* on Windows 95/NT.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <direct.h>
|
||||
|
||||
/* struct dirent - same as Unix */
|
||||
|
||||
struct dirent {
|
||||
long d_ino; /* inode (always 1 in WIN32) */
|
||||
off_t d_off; /* offset to this dirent */
|
||||
unsigned short d_reclen; /* length of d_name */
|
||||
char d_name[_MAX_FNAME + 1]; /* filename (null terminated) */
|
||||
};
|
||||
|
||||
|
||||
/* typedef DIR - not the same as Unix */
|
||||
typedef struct {
|
||||
HANDLE handle; /* _findfirst/_findnext handle */
|
||||
short offset; /* offset into directory */
|
||||
short finished; /* 1 if there are not more files */
|
||||
WIN32_FIND_DATA fileinfo; /* from _findfirst/_findnext */
|
||||
char *dir; /* the dir we are reading */
|
||||
struct dirent dent; /* the dirent to return */
|
||||
} DIR;
|
||||
|
||||
/* Function prototypes */
|
||||
DIR *opendir(const char *);
|
||||
struct dirent *readdir(DIR *);
|
||||
int readdir_r(DIR *, struct dirent *, struct dirent **);
|
||||
int closedir(DIR *);
|
||||
int rewinddir(DIR *);
|
||||
|
||||
#endif /* READDIR_H */
|
||||
+69
-36
@@ -1,17 +1,17 @@
|
||||
dnl Copyright (c) 1999, 2000 Sascha Schumann. All rights reserved.
|
||||
dnl
|
||||
dnl
|
||||
dnl Redistribution and use in source and binary forms, with or without
|
||||
dnl modification, are permitted provided that the following conditions
|
||||
dnl are met:
|
||||
dnl
|
||||
dnl
|
||||
dnl 1. Redistributions of source code must retain the above copyright
|
||||
dnl notice, this list of conditions and the following disclaimer.
|
||||
dnl
|
||||
dnl
|
||||
dnl 2. Redistributions in binary form must reproduce the above copyright
|
||||
dnl notice, this list of conditions and the following disclaimer in
|
||||
dnl the documentation and/or other materials provided with the
|
||||
dnl distribution.
|
||||
dnl
|
||||
dnl
|
||||
dnl THIS SOFTWARE IS PROVIDED BY SASCHA SCHUMANN ``AS IS'' AND ANY
|
||||
dnl EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
dnl IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
@@ -28,7 +28,7 @@ dnl OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
dnl
|
||||
dnl PTHREADS_FLAGS
|
||||
dnl
|
||||
dnl Set some magic defines to achieve POSIX threads conformance.
|
||||
dnl Set some magic defines to achieve POSIX threads conformance
|
||||
dnl
|
||||
AC_DEFUN([PTHREADS_FLAGS],[
|
||||
if test -z "$host_alias" && test -n "$host"; then
|
||||
@@ -52,20 +52,21 @@ AC_DEFUN([PTHREADS_FLAGS],[
|
||||
PTHREAD_FLAGS=-D_REENTRANT;;
|
||||
*sco*)
|
||||
PTHREAD_FLAGS=-D_REENTRANT;;
|
||||
dnl Solves sigwait() problem, creates problems with u_long etc.
|
||||
dnl PTHREAD_FLAGS="-D_REENTRANT -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=199506 -D_XOPEN_SOURCE_EXTENDED=1";;
|
||||
esac
|
||||
|
||||
if test -n "$PTHREAD_FLAGS"; then
|
||||
CPPFLAGS="$CPPFLAGS $PTHREAD_FLAGS"
|
||||
fi
|
||||
])
|
||||
|
||||
])dnl
|
||||
dnl
|
||||
dnl PTHREADS_CHECK_COMPILE
|
||||
dnl
|
||||
dnl Check whether the current setup can use POSIX threads calls.
|
||||
dnl Check whether the current setup can use POSIX threads calls
|
||||
dnl
|
||||
AC_DEFUN([PTHREADS_CHECK_COMPILE], [
|
||||
AC_LINK_IFELSE([ AC_LANG_SOURCE([
|
||||
AC_TRY_RUN( [
|
||||
#include <pthread.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -79,17 +80,23 @@ int main() {
|
||||
int data = 1;
|
||||
pthread_mutexattr_init(&mattr);
|
||||
return pthread_create(&thd, NULL, thread_routine, &data);
|
||||
} ]) ], [
|
||||
pthreads_checked=yes
|
||||
} ], [
|
||||
pthreads_working=yes
|
||||
], [
|
||||
pthreads_checked=no
|
||||
]
|
||||
) ] )
|
||||
|
||||
pthreads_working=no
|
||||
], [
|
||||
dnl For cross compiling running this test is of no use. NetWare supports pthreads
|
||||
pthreads_working=no
|
||||
case $host_alias in
|
||||
*netware*)
|
||||
pthreads_working=yes
|
||||
esac
|
||||
]
|
||||
) ] )dnl
|
||||
dnl
|
||||
dnl PTHREADS_CHECK
|
||||
dnl PTHREADS_CHECK()
|
||||
dnl
|
||||
dnl Try to find a way to enable POSIX threads.
|
||||
dnl Try to find a way to enable POSIX threads
|
||||
dnl
|
||||
dnl Magic flags
|
||||
dnl -kthread gcc (FreeBSD)
|
||||
@@ -97,36 +104,49 @@ dnl -Kthread UDK cc (UnixWare)
|
||||
dnl -mt WorkShop cc (Solaris)
|
||||
dnl -mthreads gcc (AIX)
|
||||
dnl -pthread gcc (Linux, FreeBSD, NetBSD, OpenBSD)
|
||||
dnl -pthreads gcc (Solaris)
|
||||
dnl -pthreads gcc (Solaris)
|
||||
dnl -qthreaded AIX cc V5
|
||||
dnl -threads gcc (HP-UX)
|
||||
dnl
|
||||
AC_DEFUN([PTHREADS_CHECK],[
|
||||
AC_CACHE_CHECK(for pthreads_cflags,ac_cv_pthreads_cflags,[
|
||||
ac_cv_pthreads_cflags=
|
||||
if test "$pthreads_working" != "yes"; then
|
||||
for flag in -kthread -pthread -pthreads -mthreads -Kthread -threads -mt -qthreaded; do
|
||||
ac_save=$CFLAGS
|
||||
CFLAGS="$CFLAGS $flag"
|
||||
PTHREADS_CHECK_COMPILE
|
||||
CFLAGS=$ac_save
|
||||
if test "$pthreads_checked" = "yes"; then
|
||||
ac_cv_pthreads_cflags=$flag
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if test "$beos_threads" = "1"; then
|
||||
pthreads_working="yes"
|
||||
ac_cv_pthreads_cflags=""
|
||||
else
|
||||
save_CFLAGS=$CFLAGS
|
||||
save_LIBS=$LIBS
|
||||
PTHREADS_ASSIGN_VARS
|
||||
PTHREADS_CHECK_COMPILE
|
||||
LIBS=$save_LIBS
|
||||
CFLAGS=$save_CFLAGS
|
||||
|
||||
AC_CACHE_CHECK(for pthreads_cflags,ac_cv_pthreads_cflags,[
|
||||
ac_cv_pthreads_cflags=
|
||||
if test "$pthreads_working" != "yes"; then
|
||||
for flag in -kthread -pthread -pthreads -mthreads -Kthread -threads -mt -qthreaded; do
|
||||
ac_save=$CFLAGS
|
||||
CFLAGS="$CFLAGS $flag"
|
||||
PTHREADS_CHECK_COMPILE
|
||||
CFLAGS=$ac_save
|
||||
if test "$pthreads_working" = "yes"; then
|
||||
ac_cv_pthreads_cflags=$flag
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CACHE_CHECK(for pthreads_lib, ac_cv_pthreads_lib,[
|
||||
ac_cv_pthreads_lib=
|
||||
if test "$pthreads_working" != "yes"; then
|
||||
for lib in pthread pthreads c_r; do
|
||||
for lib in pthread pthreads c_r; do
|
||||
ac_save=$LIBS
|
||||
LIBS="$LIBS -l$lib"
|
||||
PTHREADS_CHECK_COMPILE
|
||||
LIBS=$ac_save
|
||||
if test "$pthreads_checked" = "yes"; then
|
||||
if test "$pthreads_working" = "yes"; then
|
||||
ac_cv_pthreads_lib=$lib
|
||||
break
|
||||
fi
|
||||
@@ -134,7 +154,20 @@ if test "$pthreads_working" != "yes"; then
|
||||
fi
|
||||
])
|
||||
|
||||
if test "x$ac_cv_pthreads_cflags" != "x" -o "x$ac_cv_pthreads_lib" != "x"; then
|
||||
pthreads_working="yes"
|
||||
if test "$pthreads_working" = "yes"; then
|
||||
threads_result="POSIX-Threads found"
|
||||
else
|
||||
threads_result="POSIX-Threads not found"
|
||||
fi
|
||||
])
|
||||
])dnl
|
||||
dnl
|
||||
dnl
|
||||
AC_DEFUN([PTHREADS_ASSIGN_VARS],[
|
||||
if test -n "$ac_cv_pthreads_lib"; then
|
||||
LIBS="$LIBS -l$ac_cv_pthreads_lib"
|
||||
fi
|
||||
|
||||
if test -n "$ac_cv_pthreads_cflags"; then
|
||||
CFLAGS="$CFLAGS $ac_cv_pthreads_cflags"
|
||||
fi
|
||||
])dnl
|
||||
+124
-11
@@ -1,17 +1,130 @@
|
||||
dnl This file contains TSRM specific autoconf macros.
|
||||
|
||||
dnl
|
||||
dnl TSRM_CHECK_PTHREADS
|
||||
dnl
|
||||
AC_DEFUN([TSRM_CHECK_PTHREADS],[
|
||||
PTHREADS_CHECK
|
||||
dnl TSRM_CHECK_GCC_ARG(ARG, ACTION-IF-FOUND, ACTION-IF-NOT_FOUND)
|
||||
AC_DEFUN([TSRM_CHECK_GCC_ARG],[
|
||||
gcc_arg_name=[ac_cv_gcc_arg]translit($1,A-Z-,a-z_)
|
||||
AC_CACHE_CHECK([whether $CC supports $1], [ac_cv_gcc_arg]translit($1,A-Z-,a-z_), [
|
||||
echo 'void somefunc() { };' > conftest.c
|
||||
cmd='$CC $1 -c conftest.c'
|
||||
if eval $cmd 2>&1 | egrep -e $1 >/dev/null ; then
|
||||
ac_result=no
|
||||
else
|
||||
ac_result=yes
|
||||
fi
|
||||
eval $gcc_arg_name=$ac_result
|
||||
rm -f conftest.*
|
||||
])
|
||||
if eval test "\$$gcc_arg_name" = "yes"; then
|
||||
$2
|
||||
else
|
||||
:
|
||||
$3
|
||||
fi
|
||||
])
|
||||
|
||||
if test "$pthreads_working" != "yes"; then
|
||||
AC_MSG_ERROR(Your system seems to lack POSIX threads.)
|
||||
AC_DEFUN([TSRM_BASIC_CHECKS],[
|
||||
|
||||
AC_REQUIRE([AC_PROG_CC])dnl
|
||||
dnl AC_REQUIRE([AM_PROG_CC_STDC])dnl
|
||||
AC_REQUIRE([AC_PROG_CC_C_O])dnl
|
||||
AC_REQUIRE([AC_PROG_RANLIB])dnl
|
||||
|
||||
AC_CHECK_HEADERS(stdarg.h)
|
||||
|
||||
AC_CHECK_FUNCS(sigprocmask)
|
||||
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TSRM_CHECK_PTH],[
|
||||
|
||||
AC_MSG_CHECKING(for GNU Pth)
|
||||
PTH_PREFIX="`$1 --prefix`"
|
||||
if test -z "$PTH_PREFIX"; then
|
||||
AC_MSG_RESULT(Please check your Pth installation)
|
||||
fi
|
||||
|
||||
AC_DEFINE(PTHREADS, 1, Whether to use Pthreads)
|
||||
CPPFLAGS="$CPPFLAGS `$1 --cflags`"
|
||||
LDFLAGS="$LDFLAGS `$1 --ldflags`"
|
||||
LIBS="$LIBS `$1 --libs`"
|
||||
|
||||
AC_DEFINE(GNUPTH, 1, [Whether you use GNU Pth])
|
||||
AC_MSG_RESULT(yes - installed in $PTH_PREFIX)
|
||||
|
||||
])
|
||||
|
||||
AC_DEFUN([TSRM_CHECK_ST],[
|
||||
if test -r "$1/include/st.h"; then
|
||||
CPPFLAGS="$CPPFLAGS -I$1/include"
|
||||
LDFLAGS="$LDFLAGS -L$1/lib"
|
||||
elif test -r "$1/st.h"; then
|
||||
CPPFLAGS="$CPPFLAGS -I$1"
|
||||
LDFLAGS="$LDFLAGS -L$1"
|
||||
fi
|
||||
AC_CHECK_HEADERS(st.h,[],[
|
||||
AC_MSG_ERROR([Sorry[,] I was unable to locate the State Threads header file. Please specify the prefix using --with-tsrm-st=/prefix])
|
||||
])
|
||||
LIBS="$LIBS -lst"
|
||||
AC_MSG_CHECKING(for SGI's State Threads)
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(TSRM_ST, 1, [ ])
|
||||
])
|
||||
|
||||
sinclude(threads.m4)
|
||||
|
||||
AC_DEFUN([TSRM_CHECK_PTHREADS],[
|
||||
|
||||
PTHREADS_CHECK
|
||||
|
||||
if test "$beos_threads" = "1"; then
|
||||
AC_DEFINE(BETHREADS, 1, Whether to use native BeOS threads)
|
||||
else
|
||||
if test "$pthreads_working" != "yes"; then
|
||||
AC_MSG_ERROR(Your system seems to lack POSIX threads.)
|
||||
fi
|
||||
|
||||
AC_DEFINE(PTHREADS, 1, Whether to use Pthreads)
|
||||
|
||||
AC_MSG_CHECKING(for POSIX threads)
|
||||
AC_MSG_RESULT(yes)
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TSRM_THREADS_CHECKS],[
|
||||
|
||||
dnl For the thread implementations, we always use --with-*
|
||||
dnl to maintain consistency
|
||||
|
||||
AC_ARG_WITH(tsrm-pth,
|
||||
[ --with-tsrm-pth[=pth-config]
|
||||
Use GNU Pth],[
|
||||
TSRM_PTH=$withval
|
||||
],[
|
||||
TSRM_PTH=no
|
||||
])
|
||||
|
||||
AC_ARG_WITH(tsrm-st,
|
||||
[ --with-tsrm-st Use SGI's State Threads],[
|
||||
TSRM_ST=$withval
|
||||
],[
|
||||
TSRM_ST=no
|
||||
])
|
||||
|
||||
AC_ARG_WITH(tsrm-pthreads,
|
||||
[ --with-tsrm-pthreads Use POSIX threads (default)],[
|
||||
TSRM_PTHREADS=$withval
|
||||
],[
|
||||
TSRM_PTHREADS=yes
|
||||
])
|
||||
|
||||
test "$TSRM_PTH" = "yes" && TSRM_PTH=pth-config
|
||||
|
||||
if test "$TSRM_PTH" != "no"; then
|
||||
TSRM_CHECK_PTH($TSRM_PTH)
|
||||
elif test "$TSRM_ST" != "no"; then
|
||||
TSRM_CHECK_ST($TSRM_ST)
|
||||
elif test "$TSRM_PTHREADS" != "no"; then
|
||||
TSRM_CHECK_PTHREADS
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(for POSIX threads)
|
||||
AC_MSG_RESULT(yes)
|
||||
])
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef TSRM_CONFIG_W32_H
|
||||
#define TSRM_CONFIG_W32_H
|
||||
|
||||
#include <../main/config.w32.h>
|
||||
|
||||
#define HAVE_UTIME 1
|
||||
#define HAVE_ALLOCA 1
|
||||
#define HAVE_REALPATH 1
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <crtdbg.h>
|
||||
|
||||
#undef inline
|
||||
#ifdef ZEND_WIN32_FORCE_INLINE
|
||||
# define inline __forceinline
|
||||
#else
|
||||
# define inline
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef TSRM_CONFIG_COMMON_H
|
||||
#define TSRM_CONFIG_COMMON_H
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
# if WINNT|WIN32
|
||||
# define TSRM_WIN32
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
# include "tsrm_config.w32.h"
|
||||
#else
|
||||
# include <tsrm_config.h>
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_ALLOCA_H && !defined(_ALLOCA_H)
|
||||
# include <alloca.h>
|
||||
#endif
|
||||
|
||||
/* AIX requires this to be the first thing in the file. */
|
||||
#ifndef __GNUC__
|
||||
# ifndef HAVE_ALLOCA_H
|
||||
# ifdef _AIX
|
||||
#pragma alloca
|
||||
# else
|
||||
# ifndef alloca /* predefined by HP cc +Olibcalls */
|
||||
# ifndef NETWARE
|
||||
char *alloca ();
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
# ifdef PATH_MAX
|
||||
# define MAXPATHLEN PATH_MAX
|
||||
# elif defined(MAX_PATH)
|
||||
# define MAXPATHLEN MAX_PATH
|
||||
# else
|
||||
# define MAXPATHLEN 256
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if (HAVE_ALLOCA || (defined (__GNUC__) && __GNUC__ >= 2))
|
||||
# define TSRM_ALLOCA_MAX_SIZE 4096
|
||||
# define TSRM_ALLOCA_FLAG(name) \
|
||||
int name;
|
||||
# define tsrm_do_alloca_ex(size, limit, use_heap) \
|
||||
((use_heap = ((size) > (limit))) ? malloc(size) : alloca(size))
|
||||
# define tsrm_do_alloca(size, use_heap) \
|
||||
tsrm_do_alloca_ex(size, TSRM_ALLOCA_MAX_SIZE, use_heap)
|
||||
# define tsrm_free_alloca(p, use_heap) \
|
||||
do { if (use_heap) free(p); } while (0)
|
||||
#else
|
||||
# define TSRM_ALLOCA_FLAG(name)
|
||||
# define tsrm_do_alloca(p, use_heap) malloc(p)
|
||||
# define tsrm_free_alloca(p, use_heap) free(p)
|
||||
#endif
|
||||
|
||||
#endif /* TSRM_CONFIG_COMMON_H */
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2013 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Venkat Raghavan S <rvenkat@novell.com> |
|
||||
| Anantha Kesari H Y <hyanantha@novell.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "TSRM.h"
|
||||
|
||||
#ifdef NETWARE
|
||||
|
||||
#ifdef USE_MKFIFO
|
||||
#include <sys/stat.h>
|
||||
#elif !defined(USE_PIPE_OPEN) /* NXFifoOpen */
|
||||
#include <nks/fsio.h>
|
||||
#endif
|
||||
|
||||
#include <nks/vm.h>
|
||||
#include <nks/memory.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mktemp.h"
|
||||
|
||||
/* strtok() call in LibC is abending when used in a different address space
|
||||
* -- hence using PHP's version itself for now
|
||||
*/
|
||||
#include "tsrm_strtok_r.h"
|
||||
#define tsrm_strtok_r(a,b,c) strtok((a),(b))
|
||||
|
||||
#define WHITESPACE " \t"
|
||||
#define MAX_ARGS 10
|
||||
|
||||
|
||||
TSRM_API FILE* popen(const char *commandline, const char *type)
|
||||
{
|
||||
char *command = NULL, *argv[MAX_ARGS] = {'\0'}, **env = NULL;
|
||||
char *tempName = "sys:/php/temp/phpXXXXXX.tmp";
|
||||
char *filePath = NULL;
|
||||
char *ptr = NULL;
|
||||
int ptrLen = 0, argc = 0, i = 0, envCount = 0, err = 0;
|
||||
FILE *stream = NULL;
|
||||
#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO)
|
||||
int pipe_handle;
|
||||
int mode = O_RDONLY;
|
||||
#else
|
||||
NXHandle_t pipe_handle;
|
||||
NXMode_t mode = NX_O_RDONLY;
|
||||
#endif
|
||||
NXExecEnvSpec_t envSpec;
|
||||
NXNameSpec_t nameSpec;
|
||||
NXVmId_t newVM = 0;
|
||||
|
||||
/* Check for validity of input parameters */
|
||||
if (!commandline || !type)
|
||||
return NULL;
|
||||
|
||||
/* Get temporary file name */
|
||||
filePath = mktemp(tempName);
|
||||
if (!filePath)
|
||||
return NULL;
|
||||
|
||||
/* Set pipe mode according to type -- for now allow only "r" or "w" */
|
||||
if (strcmp(type, "r") == 0)
|
||||
#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO)
|
||||
mode = O_RDONLY;
|
||||
#else
|
||||
mode = NX_O_RDONLY;
|
||||
#endif
|
||||
else if (strcmp(type, "w") == 0)
|
||||
#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO)
|
||||
mode = O_WRONLY;
|
||||
#else
|
||||
mode = NX_O_WRONLY;
|
||||
#endif
|
||||
else
|
||||
return NULL;
|
||||
|
||||
#ifdef USE_PIPE_OPEN
|
||||
pipe_handle = pipe_open(filePath, mode);
|
||||
if (pipe_handle == -1)
|
||||
return NULL;
|
||||
#elif defined(USE_MKFIFO)
|
||||
pipe_handle = mkfifo(filePath, mode);
|
||||
if (pipe_handle == -1)
|
||||
return NULL;
|
||||
#else
|
||||
/* - NetWare doesn't require first parameter
|
||||
* - Allowing LibC to choose the buffer size for now
|
||||
*/
|
||||
err = NXFifoOpen(0, filePath, mode, 0, &pipe_handle);
|
||||
if (err)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
/* Copy the environment variables in preparation for the spawn call */
|
||||
envCount = NXGetEnvCount() + 1; /* add one for NULL */
|
||||
env = (char **) NXMemAlloc(sizeof(char *) * envCount, 0);
|
||||
if (!env)
|
||||
return NULL;
|
||||
|
||||
err = NXCopyEnv(env, envCount);
|
||||
if (err) {
|
||||
NXMemFree (env);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Separate commandline string into words */
|
||||
ptr = tsrm_strtok_r((char*)commandline, WHITESPACE, NULL);
|
||||
ptrLen = strlen(ptr);
|
||||
|
||||
command = (char*)malloc(ptrLen + 1);
|
||||
if (!command) {
|
||||
NXMemFree (env);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy (command, ptr);
|
||||
|
||||
ptr = tsrm_strtok_r(NULL, WHITESPACE, NULL);
|
||||
while (ptr && (argc < MAX_ARGS)) {
|
||||
ptrLen = strlen(ptr);
|
||||
|
||||
argv[argc] = (char*)malloc(ptrLen + 1);
|
||||
if (!argv[argc]) {
|
||||
NXMemFree (env);
|
||||
if (command)
|
||||
free (command);
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (argv[i])
|
||||
free (argv[i]);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy (argv[argc], ptr);
|
||||
argc++;
|
||||
ptr = tsrm_strtok_r(NULL, WHITESPACE, NULL);
|
||||
}
|
||||
|
||||
/* Setup the execution environment and spawn new process */
|
||||
envSpec.esFlags = 0; /* Not used */
|
||||
envSpec.esArgc = argc;
|
||||
envSpec.esArgv = (void **) argv;
|
||||
envSpec.esEnv = (void **) env;
|
||||
|
||||
/* envSpec.esStdin.ssType = */
|
||||
envSpec.esStdout.ssType = NX_OBJ_FIFO;
|
||||
envSpec.esStderr.ssType = NX_OBJ_FILE;
|
||||
|
||||
/* 'ssHandle' is not a struct/union/class member */
|
||||
/*
|
||||
envSpec.esStdin.ssHandle =
|
||||
envSpec.esStdout.ssHandle =
|
||||
envSpec.esStderr.ssHandle = -1;
|
||||
*/
|
||||
envSpec.esStdin.ssPathCtx = NULL;
|
||||
envSpec.esStdout.ssPathCtx = NULL;
|
||||
envSpec.esStderr.ssPathCtx = NULL;
|
||||
|
||||
#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO)
|
||||
if (mode == O_RDONLY) {
|
||||
#else
|
||||
if (mode == NX_O_RDONLY) {
|
||||
#endif
|
||||
envSpec.esStdin.ssPath = filePath;
|
||||
envSpec.esStdout.ssPath = stdout;
|
||||
} else { /* Write Only */
|
||||
envSpec.esStdin.ssPath = stdin;
|
||||
envSpec.esStdout.ssPath = filePath;
|
||||
}
|
||||
|
||||
envSpec.esStderr.ssPath = stdout;
|
||||
|
||||
nameSpec.ssType = NX_OBJ_FIFO;
|
||||
/* nameSpec.ssHandle = 0; */ /* 'ssHandle' is not a struct/union/class member */
|
||||
nameSpec.ssPathCtx = NULL; /* Not used */
|
||||
nameSpec.ssPath = argv[0];
|
||||
err = NXVmSpawn(&nameSpec, &envSpec, 0, &newVM);
|
||||
if (!err)
|
||||
/* Get file pointer corresponding to the pipe (file) opened */
|
||||
stream = fdopen(pipe_handle, type);
|
||||
|
||||
/* Clean-up */
|
||||
if (env)
|
||||
NXMemFree (env);
|
||||
|
||||
if (pipe_handle)
|
||||
#if defined(USE_PIPE_OPEN) || defined(USE_MKFIFO)
|
||||
close(pipe_handle);
|
||||
#else
|
||||
NXClose(pipe_handle);
|
||||
#endif
|
||||
|
||||
if (command)
|
||||
free (command);
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (argv[i])
|
||||
free (argv[i]);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
TSRM_API int pclose(FILE* stream)
|
||||
{
|
||||
int err = 0;
|
||||
NXHandle_t fd = 0;
|
||||
|
||||
/* Get the process associated with this pipe (file) handle and terminate it */
|
||||
fd = fileno(stream);
|
||||
NXClose (fd);
|
||||
|
||||
err = fclose(stream);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* NETWARE */
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2013 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Venkat Raghavan S <rvenkat@novell.com> |
|
||||
| Anantha Kesari H Y <hyanantha@novell.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TSRM_NW_H
|
||||
#define TSRM_NW_H
|
||||
|
||||
#include "TSRM.h"
|
||||
|
||||
TSRM_API FILE* popen(const char *command, const char *type);
|
||||
TSRM_API int pclose(FILE* stream);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "tsrm_config_common.h"
|
||||
#include "tsrm_strtok_r.h"
|
||||
|
||||
static inline int in_character_class(char ch, const char *delim)
|
||||
{
|
||||
while (*delim) {
|
||||
if (*delim == ch) {
|
||||
return 1;
|
||||
}
|
||||
delim++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *tsrm_strtok_r(char *s, const char *delim, char **last)
|
||||
{
|
||||
char *token;
|
||||
|
||||
if (s == NULL) {
|
||||
s = *last;
|
||||
}
|
||||
|
||||
while (*s && in_character_class(*s, delim)) {
|
||||
s++;
|
||||
}
|
||||
if (!*s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
token = s;
|
||||
|
||||
while (*s && !in_character_class(*s, delim)) {
|
||||
s++;
|
||||
}
|
||||
if (!*s) {
|
||||
*last = s;
|
||||
} else {
|
||||
*s = '\0';
|
||||
*last = s + 1;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
main()
|
||||
{
|
||||
char foo[] = "/foo/bar//\\barbara";
|
||||
char *last;
|
||||
char *token;
|
||||
|
||||
token = tsrm_strtok_r(foo, "/\\", &last);
|
||||
while (token) {
|
||||
printf ("Token = '%s'\n", token);
|
||||
token = tsrm_strtok_r(NULL, "/\\", &last);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef TSRM_STRTOK_R
|
||||
#define TSRM_STRTOK_R
|
||||
|
||||
char *tsrm_strtok_r(char *s, const char *delim, char **last);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2013 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@zend.com> |
|
||||
| Sascha Schumann <sascha@schumann.cx> |
|
||||
| Pierre Joye <pierre@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef VIRTUAL_CWD_H
|
||||
#define VIRTUAL_CWD_H
|
||||
|
||||
#include "TSRM.h"
|
||||
#include "tsrm_config_common.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifdef HAVE_UTIME_H
|
||||
#include <utime.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDARG_H
|
||||
#include <stdarg.h>
|
||||
#endif
|
||||
|
||||
#ifdef ZTS
|
||||
#define VIRTUAL_DIR
|
||||
#endif
|
||||
|
||||
#ifndef TSRM_WIN32
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#if defined(__osf__) || defined(_AIX)
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
#include "readdir.h"
|
||||
#include <sys/utime.h>
|
||||
/* mode_t isn't defined on Windows */
|
||||
typedef unsigned short mode_t;
|
||||
|
||||
#define DEFAULT_SLASH '\\'
|
||||
#define DEFAULT_DIR_SEPARATOR ';'
|
||||
#define IS_SLASH(c) ((c) == '/' || (c) == '\\')
|
||||
#define IS_SLASH_P(c) (*(c) == '/' || \
|
||||
(*(c) == '\\' && !IsDBCSLeadByte(*(c-1))))
|
||||
|
||||
/* COPY_WHEN_ABSOLUTE is 2 under Win32 because by chance both regular absolute paths
|
||||
in the file system and UNC paths need copying of two characters */
|
||||
#define COPY_WHEN_ABSOLUTE(path) 2
|
||||
#define IS_UNC_PATH(path, len) \
|
||||
(len >= 2 && IS_SLASH(path[0]) && IS_SLASH(path[1]))
|
||||
#define IS_ABSOLUTE_PATH(path, len) \
|
||||
(len >= 2 && ((isalpha(path[0]) && path[1] == ':') || IS_UNC_PATH(path, len)))
|
||||
|
||||
#elif defined(NETWARE)
|
||||
#ifdef HAVE_DIRENT_H
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_SLASH '/'
|
||||
#define DEFAULT_DIR_SEPARATOR ';'
|
||||
#define IS_SLASH(c) ((c) == '/' || (c) == '\\')
|
||||
#define IS_SLASH_P(c) IS_SLASH(*(c))
|
||||
/* Colon indicates volume name, either first character should be forward slash or backward slash */
|
||||
#define IS_ABSOLUTE_PATH(path, len) \
|
||||
((strchr(path, ':') != NULL) || ((len >= 1) && ((path[0] == '/') || (path[0] == '\\'))))
|
||||
|
||||
#else
|
||||
#ifdef HAVE_DIRENT_H
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#define DEFAULT_SLASH '/'
|
||||
|
||||
#ifdef __riscos__
|
||||
#define DEFAULT_DIR_SEPARATOR ';'
|
||||
#else
|
||||
#define DEFAULT_DIR_SEPARATOR ':'
|
||||
#endif
|
||||
|
||||
#define IS_SLASH(c) ((c) == '/')
|
||||
#define IS_SLASH_P(c) (*(c) == '/')
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef COPY_WHEN_ABSOLUTE
|
||||
#define COPY_WHEN_ABSOLUTE(path) 0
|
||||
#endif
|
||||
|
||||
#ifndef IS_ABSOLUTE_PATH
|
||||
#define IS_ABSOLUTE_PATH(path, len) \
|
||||
(IS_SLASH(path[0]))
|
||||
#endif
|
||||
|
||||
#ifdef TSRM_EXPORTS
|
||||
#define CWD_EXPORTS
|
||||
#endif
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
# ifdef CWD_EXPORTS
|
||||
# define CWD_API __declspec(dllexport)
|
||||
# else
|
||||
# define CWD_API __declspec(dllimport)
|
||||
# endif
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define CWD_API __attribute__ ((visibility("default")))
|
||||
#else
|
||||
# define CWD_API
|
||||
#endif
|
||||
|
||||
#ifdef TSRM_WIN32
|
||||
CWD_API int php_sys_stat_ex(const char *path, struct stat *buf, int lstat);
|
||||
# define php_sys_stat(path, buf) php_sys_stat_ex(path, buf, 0)
|
||||
# define php_sys_lstat(path, buf) php_sys_stat_ex(path, buf, 1)
|
||||
CWD_API int php_sys_readlink(const char *link, char *target, size_t target_len);
|
||||
#else
|
||||
# define php_sys_stat stat
|
||||
# define php_sys_lstat lstat
|
||||
# ifdef HAVE_SYMLINK
|
||||
# define php_sys_readlink(link, target, target_len) readlink(link, target, target_len)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef struct _cwd_state {
|
||||
char *cwd;
|
||||
int cwd_length;
|
||||
} cwd_state;
|
||||
|
||||
typedef int (*verify_path_func)(const cwd_state *);
|
||||
|
||||
CWD_API void virtual_cwd_startup(void);
|
||||
CWD_API void virtual_cwd_shutdown(void);
|
||||
CWD_API char *virtual_getcwd_ex(size_t *length TSRMLS_DC);
|
||||
CWD_API char *virtual_getcwd(char *buf, size_t size TSRMLS_DC);
|
||||
CWD_API int virtual_chdir(const char *path TSRMLS_DC);
|
||||
CWD_API int virtual_chdir_file(const char *path, int (*p_chdir)(const char *path TSRMLS_DC) TSRMLS_DC);
|
||||
CWD_API int virtual_filepath(const char *path, char **filepath TSRMLS_DC);
|
||||
CWD_API int virtual_filepath_ex(const char *path, char **filepath, verify_path_func verify_path TSRMLS_DC);
|
||||
CWD_API char *virtual_realpath(const char *path, char *real_path TSRMLS_DC);
|
||||
CWD_API FILE *virtual_fopen(const char *path, const char *mode TSRMLS_DC);
|
||||
CWD_API int virtual_open(const char *path TSRMLS_DC, int flags, ...);
|
||||
CWD_API int virtual_creat(const char *path, mode_t mode TSRMLS_DC);
|
||||
CWD_API int virtual_rename(char *oldname, char *newname TSRMLS_DC);
|
||||
CWD_API int virtual_stat(const char *path, struct stat *buf TSRMLS_DC);
|
||||
CWD_API int virtual_lstat(const char *path, struct stat *buf TSRMLS_DC);
|
||||
CWD_API int virtual_unlink(const char *path TSRMLS_DC);
|
||||
CWD_API int virtual_mkdir(const char *pathname, mode_t mode TSRMLS_DC);
|
||||
CWD_API int virtual_rmdir(const char *pathname TSRMLS_DC);
|
||||
CWD_API DIR *virtual_opendir(const char *pathname TSRMLS_DC);
|
||||
CWD_API FILE *virtual_popen(const char *command, const char *type TSRMLS_DC);
|
||||
CWD_API int virtual_access(const char *pathname, int mode TSRMLS_DC);
|
||||
#if defined(TSRM_WIN32)
|
||||
/* these are not defined in win32 headers */
|
||||
#ifndef W_OK
|
||||
#define W_OK 0x02
|
||||
#endif
|
||||
#ifndef R_OK
|
||||
#define R_OK 0x04
|
||||
#endif
|
||||
#ifndef X_OK
|
||||
#define X_OK 0x01
|
||||
#endif
|
||||
#ifndef F_OK
|
||||
#define F_OK 0x00
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if HAVE_UTIME
|
||||
CWD_API int virtual_utime(const char *filename, struct utimbuf *buf TSRMLS_DC);
|
||||
#endif
|
||||
CWD_API int virtual_chmod(const char *filename, mode_t mode TSRMLS_DC);
|
||||
#if !defined(TSRM_WIN32) && !defined(NETWARE)
|
||||
CWD_API int virtual_chown(const char *filename, uid_t owner, gid_t group, int link TSRMLS_DC);
|
||||
#endif
|
||||
|
||||
/* One of the following constants must be used as the last argument
|
||||
in virtual_file_ex() call. */
|
||||
|
||||
#define CWD_EXPAND 0 /* expand "." and ".." but dont resolve symlinks */
|
||||
#define CWD_FILEPATH 1 /* resolve symlinks if file is exist otherwise expand */
|
||||
#define CWD_REALPATH 2 /* call realpath(), resolve symlinks. File must exist */
|
||||
|
||||
CWD_API int virtual_file_ex(cwd_state *state, const char *path, verify_path_func verify_path, int use_realpath TSRMLS_DC);
|
||||
|
||||
CWD_API char *tsrm_realpath(const char *path, char *real_path TSRMLS_DC);
|
||||
|
||||
#define REALPATH_CACHE_TTL (2*60) /* 2 minutes */
|
||||
#define REALPATH_CACHE_SIZE 0 /* disabled while php.ini isn't loaded */
|
||||
|
||||
typedef struct _realpath_cache_bucket {
|
||||
unsigned long key;
|
||||
char *path;
|
||||
int path_len;
|
||||
char *realpath;
|
||||
int realpath_len;
|
||||
int is_dir;
|
||||
time_t expires;
|
||||
#ifdef PHP_WIN32
|
||||
unsigned char is_rvalid;
|
||||
unsigned char is_readable;
|
||||
unsigned char is_wvalid;
|
||||
unsigned char is_writable;
|
||||
#endif
|
||||
struct _realpath_cache_bucket *next;
|
||||
} realpath_cache_bucket;
|
||||
|
||||
typedef struct _virtual_cwd_globals {
|
||||
cwd_state cwd;
|
||||
long realpath_cache_size;
|
||||
long realpath_cache_size_limit;
|
||||
long realpath_cache_ttl;
|
||||
realpath_cache_bucket *realpath_cache[1024];
|
||||
} virtual_cwd_globals;
|
||||
|
||||
#ifdef ZTS
|
||||
extern ts_rsrc_id cwd_globals_id;
|
||||
# define CWDG(v) TSRMG(cwd_globals_id, virtual_cwd_globals *, v)
|
||||
#else
|
||||
extern virtual_cwd_globals cwd_globals;
|
||||
# define CWDG(v) (cwd_globals.v)
|
||||
#endif
|
||||
|
||||
CWD_API void realpath_cache_clean(TSRMLS_D);
|
||||
CWD_API void realpath_cache_del(const char *path, int path_len TSRMLS_DC);
|
||||
CWD_API realpath_cache_bucket* realpath_cache_lookup(const char *path, int path_len, time_t t TSRMLS_DC);
|
||||
CWD_API int realpath_cache_size(TSRMLS_D);
|
||||
CWD_API int realpath_cache_max_buckets(TSRMLS_D);
|
||||
CWD_API realpath_cache_bucket** realpath_cache_get_buckets(TSRMLS_D);
|
||||
|
||||
/* The actual macros to be used in programs using TSRM
|
||||
* If the program defines VIRTUAL_DIR it will use the
|
||||
* virtual_* functions
|
||||
*/
|
||||
|
||||
#ifdef VIRTUAL_DIR
|
||||
|
||||
#define VCWD_GETCWD(buff, size) virtual_getcwd(buff, size TSRMLS_CC)
|
||||
#define VCWD_FOPEN(path, mode) virtual_fopen(path, mode TSRMLS_CC)
|
||||
/* Because open() has two modes, we have to macros to replace it */
|
||||
#define VCWD_OPEN(path, flags) virtual_open(path TSRMLS_CC, flags)
|
||||
#define VCWD_OPEN_MODE(path, flags, mode) virtual_open(path TSRMLS_CC, flags, mode)
|
||||
#define VCWD_CREAT(path, mode) virtual_creat(path, mode TSRMLS_CC)
|
||||
#define VCWD_CHDIR(path) virtual_chdir(path TSRMLS_CC)
|
||||
#define VCWD_CHDIR_FILE(path) virtual_chdir_file(path, virtual_chdir TSRMLS_CC)
|
||||
#define VCWD_GETWD(buf)
|
||||
#define VCWD_REALPATH(path, real_path) virtual_realpath(path, real_path TSRMLS_CC)
|
||||
#define VCWD_RENAME(oldname, newname) virtual_rename(oldname, newname TSRMLS_CC)
|
||||
#define VCWD_STAT(path, buff) virtual_stat(path, buff TSRMLS_CC)
|
||||
# define VCWD_LSTAT(path, buff) virtual_lstat(path, buff TSRMLS_CC)
|
||||
#define VCWD_UNLINK(path) virtual_unlink(path TSRMLS_CC)
|
||||
#define VCWD_MKDIR(pathname, mode) virtual_mkdir(pathname, mode TSRMLS_CC)
|
||||
#define VCWD_RMDIR(pathname) virtual_rmdir(pathname TSRMLS_CC)
|
||||
#define VCWD_OPENDIR(pathname) virtual_opendir(pathname TSRMLS_CC)
|
||||
#define VCWD_POPEN(command, type) virtual_popen(command, type TSRMLS_CC)
|
||||
#define VCWD_ACCESS(pathname, mode) virtual_access(pathname, mode TSRMLS_CC)
|
||||
#if HAVE_UTIME
|
||||
#define VCWD_UTIME(path, time) virtual_utime(path, time TSRMLS_CC)
|
||||
#endif
|
||||
#define VCWD_CHMOD(path, mode) virtual_chmod(path, mode TSRMLS_CC)
|
||||
#if !defined(TSRM_WIN32) && !defined(NETWARE)
|
||||
#define VCWD_CHOWN(path, owner, group) virtual_chown(path, owner, group, 0 TSRMLS_CC)
|
||||
#if HAVE_LCHOWN
|
||||
#define VCWD_LCHOWN(path, owner, group) virtual_chown(path, owner, group, 1 TSRMLS_CC)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define VCWD_GETCWD(buff, size) getcwd(buff, size)
|
||||
#define VCWD_FOPEN(path, mode) fopen(path, mode)
|
||||
#define VCWD_OPEN(path, flags) open(path, flags)
|
||||
#define VCWD_OPEN_MODE(path, flags, mode) open(path, flags, mode)
|
||||
#define VCWD_CREAT(path, mode) creat(path, mode)
|
||||
/* rename on windows will fail if newname already exists.
|
||||
MoveFileEx has to be used */
|
||||
#if defined(TSRM_WIN32)
|
||||
# define VCWD_RENAME(oldname, newname) (MoveFileEx(oldname, newname, MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED) == 0 ? -1 : 0)
|
||||
#else
|
||||
# define VCWD_RENAME(oldname, newname) rename(oldname, newname)
|
||||
#endif
|
||||
#define VCWD_CHDIR(path) chdir(path)
|
||||
#define VCWD_CHDIR_FILE(path) virtual_chdir_file(path, chdir)
|
||||
#define VCWD_GETWD(buf) getwd(buf)
|
||||
#define VCWD_STAT(path, buff) php_sys_stat(path, buff)
|
||||
#define VCWD_LSTAT(path, buff) lstat(path, buff)
|
||||
#define VCWD_UNLINK(path) unlink(path)
|
||||
#define VCWD_MKDIR(pathname, mode) mkdir(pathname, mode)
|
||||
#define VCWD_RMDIR(pathname) rmdir(pathname)
|
||||
#define VCWD_OPENDIR(pathname) opendir(pathname)
|
||||
#define VCWD_POPEN(command, type) popen(command, type)
|
||||
#if defined(TSRM_WIN32)
|
||||
#define VCWD_ACCESS(pathname, mode) tsrm_win32_access(pathname, mode TSRMLS_CC)
|
||||
#else
|
||||
#define VCWD_ACCESS(pathname, mode) access(pathname, mode)
|
||||
#endif
|
||||
|
||||
#define VCWD_REALPATH(path, real_path) tsrm_realpath(path, real_path TSRMLS_CC)
|
||||
|
||||
#if HAVE_UTIME
|
||||
# ifdef TSRM_WIN32
|
||||
# define VCWD_UTIME(path, time) win32_utime(path, time)
|
||||
# else
|
||||
# define VCWD_UTIME(path, time) utime(path, time)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define VCWD_CHMOD(path, mode) chmod(path, mode)
|
||||
#if !defined(TSRM_WIN32) && !defined(NETWARE)
|
||||
#define VCWD_CHOWN(path, owner, group) chown(path, owner, group)
|
||||
#if HAVE_LCHOWN
|
||||
#define VCWD_LCHOWN(path, owner, group) lchown(path, owner, group)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* VIRTUAL_CWD_H */
|
||||
+262
-364
@@ -1,11 +1,13 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2013 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| 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. |
|
||||
@@ -14,6 +16,8 @@
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
@@ -28,8 +32,7 @@
|
||||
#ifdef TSRM_WIN32
|
||||
#include <Sddl.h>
|
||||
#include "tsrm_win32.h"
|
||||
#include "zend_virtual_cwd.h"
|
||||
#include "win32/ioutil.h"
|
||||
#include "tsrm_virtual_cwd.h"
|
||||
|
||||
#ifdef ZTS
|
||||
static ts_rsrc_id win32_globals_id;
|
||||
@@ -37,19 +40,16 @@ static ts_rsrc_id win32_globals_id;
|
||||
static tsrm_win32_globals win32_globals;
|
||||
#endif
|
||||
|
||||
static void tsrm_win32_ctor(tsrm_win32_globals *globals)
|
||||
{/*{{{*/
|
||||
#ifdef ZTS
|
||||
TSRMLS_CACHE_UPDATE();
|
||||
#endif
|
||||
static void tsrm_win32_ctor(tsrm_win32_globals *globals TSRMLS_DC)
|
||||
{
|
||||
globals->process = NULL;
|
||||
globals->shm = NULL;
|
||||
globals->process_size = 0;
|
||||
globals->shm_size = 0;
|
||||
globals->comspec = _strdup("cmd.exe");
|
||||
globals->comspec = _strdup((GetVersion()<0x80000000)?"cmd.exe":"command.com");
|
||||
|
||||
/* Set it to INVALID_HANDLE_VALUE
|
||||
* It will be initialized correctly in tsrm_win32_access or set to
|
||||
* It will be initialized correctly in tsrm_win32_access or set to
|
||||
* NULL if no impersonation has been done.
|
||||
* the impersonated token can't be set here as the impersonation
|
||||
* will happen later, in fcgi_accept_request (or whatever is the
|
||||
@@ -57,10 +57,10 @@ TSRMLS_CACHE_UPDATE();
|
||||
*/
|
||||
globals->impersonation_token = INVALID_HANDLE_VALUE;
|
||||
globals->impersonation_token_sid = NULL;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
static void tsrm_win32_dtor(tsrm_win32_globals *globals)
|
||||
{/*{{{*/
|
||||
static void tsrm_win32_dtor(tsrm_win32_globals *globals TSRMLS_DC)
|
||||
{
|
||||
shm_pair *ptr;
|
||||
|
||||
if (globals->process) {
|
||||
@@ -85,63 +85,65 @@ static void tsrm_win32_dtor(tsrm_win32_globals *globals)
|
||||
if (globals->impersonation_token_sid) {
|
||||
free(globals->impersonation_token_sid);
|
||||
}
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
TSRM_API void tsrm_win32_startup(void)
|
||||
{/*{{{*/
|
||||
{
|
||||
#ifdef ZTS
|
||||
ts_allocate_id(&win32_globals_id, sizeof(tsrm_win32_globals), (ts_allocate_ctor)tsrm_win32_ctor, (ts_allocate_ctor)tsrm_win32_dtor);
|
||||
#else
|
||||
tsrm_win32_ctor(&win32_globals);
|
||||
tsrm_win32_ctor(&win32_globals TSRMLS_CC);
|
||||
#endif
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
TSRM_API void tsrm_win32_shutdown(void)
|
||||
{/*{{{*/
|
||||
{
|
||||
#ifndef ZTS
|
||||
tsrm_win32_dtor(&win32_globals);
|
||||
tsrm_win32_dtor(&win32_globals TSRMLS_CC);
|
||||
#endif
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
const char * tsrm_win32_get_path_sid_key(const char *pathname, size_t pathname_len, size_t *key_len)
|
||||
{/*{{{*/
|
||||
char * tsrm_win32_get_path_sid_key(const char *pathname TSRMLS_DC)
|
||||
{
|
||||
PSID pSid = TWG(impersonation_token_sid);
|
||||
char *ptcSid = NULL;
|
||||
DWORD sid_len = pSid ? GetLengthSid(pSid) : 0;
|
||||
TCHAR *ptcSid = NULL;
|
||||
char *bucket_key = NULL;
|
||||
size_t ptc_sid_len;
|
||||
|
||||
if (!pSid) {
|
||||
*key_len = pathname_len;
|
||||
return pathname;
|
||||
bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname) + 1);
|
||||
if (!bucket_key) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(bucket_key, pathname, strlen(pathname));
|
||||
return bucket_key;
|
||||
}
|
||||
|
||||
if (!ConvertSidToStringSid(pSid, &ptcSid)) {
|
||||
*key_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ptc_sid_len = strlen(ptcSid);
|
||||
*key_len = pathname_len + ptc_sid_len;
|
||||
bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *key_len + 1);
|
||||
bucket_key = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, strlen(pathname) + strlen(ptcSid) + 1);
|
||||
if (!bucket_key) {
|
||||
LocalFree(ptcSid);
|
||||
*key_len = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(bucket_key, ptcSid, ptc_sid_len);
|
||||
memcpy(bucket_key + ptc_sid_len, pathname, pathname_len + 1);
|
||||
memcpy(bucket_key, ptcSid, strlen(ptcSid));
|
||||
memcpy(bucket_key + strlen(ptcSid), pathname, strlen(pathname) + 1);
|
||||
|
||||
LocalFree(ptcSid);
|
||||
return bucket_key;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
PSID tsrm_win32_get_token_sid(HANDLE hToken)
|
||||
{/*{{{*/
|
||||
{
|
||||
BOOL bSuccess = FALSE;
|
||||
DWORD dwLength = 0;
|
||||
PTOKEN_USER pTokenUser = NULL;
|
||||
PSID sid;
|
||||
PSID *ppsid = &sid;
|
||||
DWORD sid_len;
|
||||
PSID pResultSid = NULL;
|
||||
|
||||
@@ -186,10 +188,10 @@ Finished:
|
||||
HeapFree(GetProcessHeap(), 0, (LPVOID)pTokenUser);
|
||||
}
|
||||
return NULL;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
TSRM_API int tsrm_win32_access(const char *pathname, int mode)
|
||||
{/*{{{*/
|
||||
TSRM_API int tsrm_win32_access(const char *pathname, int mode TSRMLS_DC)
|
||||
{
|
||||
time_t t;
|
||||
HANDLE thread_token = NULL;
|
||||
PSID token_sid;
|
||||
@@ -202,176 +204,185 @@ TSRM_API int tsrm_win32_access(const char *pathname, int mode)
|
||||
BYTE * psec_desc = NULL;
|
||||
BOOL fAccess = FALSE;
|
||||
|
||||
BOOL bucket_key_alloc = FALSE;
|
||||
realpath_cache_bucket * bucket = NULL;
|
||||
char real_path[MAXPATHLEN] = {0};
|
||||
char * real_path = NULL;
|
||||
|
||||
if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
|
||||
if(tsrm_realpath(pathname, real_path) == NULL) {
|
||||
SET_ERRNO_FROM_WIN32_CODE(ERROR_FILE_NOT_FOUND);
|
||||
return -1;
|
||||
if (mode == 1 /*X_OK*/) {
|
||||
DWORD type;
|
||||
return GetBinaryType(pathname, &type) ? 0 : -1;
|
||||
} else {
|
||||
if(!IS_ABSOLUTE_PATH(pathname, strlen(pathname)+1)) {
|
||||
real_path = (char *)malloc(MAX_PATH);
|
||||
if(tsrm_realpath(pathname, real_path TSRMLS_CC) == NULL) {
|
||||
goto Finished;
|
||||
}
|
||||
pathname = real_path;
|
||||
}
|
||||
|
||||
if(access(pathname, mode)) {
|
||||
free(real_path);
|
||||
return errno;
|
||||
}
|
||||
pathname = real_path;
|
||||
}
|
||||
|
||||
PHP_WIN32_IOUTIL_INIT_W(pathname)
|
||||
if (!pathw) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Either access call failed, or the mode was asking for a specific check.*/
|
||||
int ret = php_win32_ioutil_access_w(pathw, mode);
|
||||
if (0 > ret || X_OK == mode || F_OK == mode) {
|
||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
||||
return ret;
|
||||
}
|
||||
/* If only existence check is made, return now */
|
||||
if (mode == 0) {
|
||||
free(real_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Only in NTS when impersonate==1 (aka FastCGI) */
|
||||
|
||||
/*
|
||||
AccessCheck() requires an impersonation token. We first get a primary
|
||||
token and then create a duplicate impersonation token. The
|
||||
impersonation token is not actually assigned to the thread, but is
|
||||
used in the call to AccessCheck. Thus, this function itself never
|
||||
impersonates, but does use the identity of the thread. If the thread
|
||||
was impersonating already, this function uses that impersonation context.
|
||||
*/
|
||||
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
|
||||
if (GetLastError() == ERROR_NO_TOKEN) {
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
|
||||
TWG(impersonation_token) = NULL;
|
||||
goto Finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* token_sid will be freed in tsrmwin32_dtor */
|
||||
token_sid = tsrm_win32_get_token_sid(thread_token);
|
||||
if (!token_sid) {
|
||||
if (TWG(impersonation_token_sid)) {
|
||||
free(TWG(impersonation_token_sid));
|
||||
}
|
||||
TWG(impersonation_token_sid) = NULL;
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
/* Different identity, we need a new impersonated token as well */
|
||||
if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) {
|
||||
if (TWG(impersonation_token_sid)) {
|
||||
free(TWG(impersonation_token_sid));
|
||||
}
|
||||
TWG(impersonation_token_sid) = token_sid;
|
||||
|
||||
/* Duplicate the token as impersonated token */
|
||||
if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) {
|
||||
goto Finished;
|
||||
}
|
||||
} else {
|
||||
/* we already have it, free it then */
|
||||
free(token_sid);
|
||||
}
|
||||
|
||||
if (CWDG(realpath_cache_size_limit)) {
|
||||
t = time(0);
|
||||
bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
|
||||
if(bucket == NULL && !real_path[0]) {
|
||||
/* We used the pathname directly. Call tsrm_realpath */
|
||||
/* so that entry is created in realpath cache */
|
||||
if(tsrm_realpath(pathname, real_path) != NULL) {
|
||||
pathname = real_path;
|
||||
bucket = realpath_cache_lookup(pathname, strlen(pathname), t);
|
||||
PHP_WIN32_IOUTIL_REINIT_W(pathname);
|
||||
/*
|
||||
AccessCheck() requires an impersonation token. We first get a primary
|
||||
token and then create a duplicate impersonation token. The
|
||||
impersonation token is not actually assigned to the thread, but is
|
||||
used in the call to AccessCheck. Thus, this function itself never
|
||||
impersonates, but does use the identity of the thread. If the thread
|
||||
was impersonating already, this function uses that impersonation context.
|
||||
*/
|
||||
if(!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &thread_token)) {
|
||||
DWORD err = GetLastError();
|
||||
if (GetLastError() == ERROR_NO_TOKEN) {
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) {
|
||||
TWG(impersonation_token) = NULL;
|
||||
goto Finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Do a full access check because access() will only check read-only attribute */
|
||||
if(mode == 0 || mode > 6) {
|
||||
if(bucket != NULL && bucket->is_rvalid) {
|
||||
fAccess = bucket->is_readable;
|
||||
/* token_sid will be freed in tsrmwin32_dtor */
|
||||
token_sid = tsrm_win32_get_token_sid(thread_token);
|
||||
if (!token_sid) {
|
||||
if (TWG(impersonation_token_sid)) {
|
||||
free(TWG(impersonation_token_sid));
|
||||
}
|
||||
TWG(impersonation_token_sid) = NULL;
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_READ;
|
||||
} else if(mode <= 2) {
|
||||
if(bucket != NULL && bucket->is_wvalid) {
|
||||
fAccess = bucket->is_writable;
|
||||
|
||||
/* Different identity, we need a new impersontated token as well */
|
||||
if (!TWG(impersonation_token_sid) || !EqualSid(token_sid, TWG(impersonation_token_sid))) {
|
||||
if (TWG(impersonation_token_sid)) {
|
||||
free(TWG(impersonation_token_sid));
|
||||
}
|
||||
TWG(impersonation_token_sid) = token_sid;
|
||||
|
||||
/* Duplicate the token as impersonated token */
|
||||
if (!DuplicateToken(thread_token, SecurityImpersonation, &TWG(impersonation_token))) {
|
||||
goto Finished;
|
||||
}
|
||||
} else {
|
||||
/* we already have it, free it then */
|
||||
free(token_sid);
|
||||
}
|
||||
|
||||
if (CWDG(realpath_cache_size_limit)) {
|
||||
t = time(0);
|
||||
bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
|
||||
if(bucket == NULL && real_path == NULL) {
|
||||
/* We used the pathname directly. Call tsrm_realpath */
|
||||
/* so that entry is created in realpath cache */
|
||||
real_path = (char *)malloc(MAX_PATH);
|
||||
if(tsrm_realpath(pathname, real_path TSRMLS_CC) != NULL) {
|
||||
pathname = real_path;
|
||||
bucket = realpath_cache_lookup(pathname, strlen(pathname), t TSRMLS_CC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Do a full access check because access() will only check read-only attribute */
|
||||
if(mode == 0 || mode > 6) {
|
||||
if(bucket != NULL && bucket->is_rvalid) {
|
||||
fAccess = bucket->is_readable;
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_READ;
|
||||
} else if(mode <= 2) {
|
||||
if(bucket != NULL && bucket->is_wvalid) {
|
||||
fAccess = bucket->is_writable;
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_WRITE;
|
||||
} else if(mode <= 4) {
|
||||
if(bucket != NULL && bucket->is_rvalid) {
|
||||
fAccess = bucket->is_readable;
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS;
|
||||
} else { // if(mode <= 6)
|
||||
if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
|
||||
fAccess = bucket->is_readable & bucket->is_writable;
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
if(TWG(impersonation_token) == NULL) {
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_WRITE;
|
||||
} else if(mode <= 4) {
|
||||
if(bucket != NULL && bucket->is_rvalid) {
|
||||
fAccess = bucket->is_readable;
|
||||
|
||||
/* Get size of security buffer. Call is expected to fail */
|
||||
if(GetFileSecurity(pathname, sec_info, NULL, 0, &sec_desc_length)) {
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS;
|
||||
} else { // if(mode <= 6)
|
||||
if(bucket != NULL && bucket->is_rvalid && bucket->is_wvalid) {
|
||||
fAccess = bucket->is_readable & bucket->is_writable;
|
||||
|
||||
psec_desc = (BYTE *)malloc(sec_desc_length);
|
||||
if(psec_desc == NULL ||
|
||||
!GetFileSecurity(pathname, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) {
|
||||
goto Finished;
|
||||
}
|
||||
desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
if(TWG(impersonation_token) == NULL) {
|
||||
goto Finished;
|
||||
}
|
||||
MapGenericMask(&desired_access, &gen_map);
|
||||
|
||||
/* Get size of security buffer. Call is expected to fail */
|
||||
if(GetFileSecurityW(pathw, sec_info, NULL, 0, &sec_desc_length)) {
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
psec_desc = (BYTE *)malloc(sec_desc_length);
|
||||
if(psec_desc == NULL ||
|
||||
!GetFileSecurityW(pathw, sec_info, (PSECURITY_DESCRIPTOR)psec_desc, sec_desc_length, &sec_desc_length)) {
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
MapGenericMask(&desired_access, &gen_map);
|
||||
|
||||
if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
|
||||
goto Finished_Impersonate;
|
||||
}
|
||||
|
||||
/* Keep the result in realpath_cache */
|
||||
if(bucket != NULL) {
|
||||
if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
|
||||
bucket->is_rvalid = 1;
|
||||
bucket->is_readable = fAccess;
|
||||
if(!AccessCheck((PSECURITY_DESCRIPTOR)psec_desc, TWG(impersonation_token), desired_access, &gen_map, &privilege_set, &priv_set_length, &granted_access, &fAccess)) {
|
||||
goto Finished_Impersonate;
|
||||
}
|
||||
else if(desired_access == FILE_GENERIC_WRITE) {
|
||||
bucket->is_wvalid = 1;
|
||||
bucket->is_writable = fAccess;
|
||||
} else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) {
|
||||
bucket->is_rvalid = 1;
|
||||
bucket->is_readable = fAccess;
|
||||
bucket->is_wvalid = 1;
|
||||
bucket->is_writable = fAccess;
|
||||
|
||||
/* Keep the result in realpath_cache */
|
||||
if(bucket != NULL) {
|
||||
if(desired_access == (FILE_GENERIC_READ|FILE_FLAG_BACKUP_SEMANTICS)) {
|
||||
bucket->is_rvalid = 1;
|
||||
bucket->is_readable = fAccess;
|
||||
}
|
||||
else if(desired_access == FILE_GENERIC_WRITE) {
|
||||
bucket->is_wvalid = 1;
|
||||
bucket->is_writable = fAccess;
|
||||
} else if (desired_access == (FILE_GENERIC_READ | FILE_GENERIC_WRITE)) {
|
||||
bucket->is_rvalid = 1;
|
||||
bucket->is_readable = fAccess;
|
||||
bucket->is_wvalid = 1;
|
||||
bucket->is_writable = fAccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Finished_Impersonate:
|
||||
if(psec_desc != NULL) {
|
||||
free(psec_desc);
|
||||
psec_desc = NULL;
|
||||
}
|
||||
if(psec_desc != NULL) {
|
||||
free(psec_desc);
|
||||
psec_desc = NULL;
|
||||
}
|
||||
|
||||
Finished:
|
||||
if(thread_token != NULL) {
|
||||
CloseHandle(thread_token);
|
||||
if(thread_token != NULL) {
|
||||
CloseHandle(thread_token);
|
||||
}
|
||||
if(real_path != NULL) {
|
||||
free(real_path);
|
||||
real_path = NULL;
|
||||
}
|
||||
|
||||
if(fAccess == FALSE) {
|
||||
errno = EACCES;
|
||||
return errno;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
||||
if(fAccess == FALSE) {
|
||||
errno = EACCES;
|
||||
return errno;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
|
||||
static process_pair *process_get(FILE *stream)
|
||||
{/*{{{*/
|
||||
static process_pair *process_get(FILE *stream TSRMLS_DC)
|
||||
{
|
||||
process_pair *ptr;
|
||||
process_pair *newptr;
|
||||
|
||||
@@ -394,12 +405,13 @@ static process_pair *process_get(FILE *stream)
|
||||
ptr = newptr + TWG(process_size);
|
||||
TWG(process_size)++;
|
||||
return ptr;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
static shm_pair *shm_get(key_t key, void *addr)
|
||||
{/*{{{*/
|
||||
static shm_pair *shm_get(int key, void *addr)
|
||||
{
|
||||
shm_pair *ptr;
|
||||
shm_pair *newptr;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
for (ptr = TWG(shm); ptr < (TWG(shm) + TWG(shm_size)); ptr++) {
|
||||
if (!ptr->descriptor) {
|
||||
@@ -424,38 +436,37 @@ static shm_pair *shm_get(key_t key, void *addr)
|
||||
TWG(shm) = newptr;
|
||||
ptr = newptr + TWG(shm_size);
|
||||
TWG(shm_size)++;
|
||||
memset(ptr, 0, sizeof(*ptr));
|
||||
return ptr;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
static HANDLE dupHandle(HANDLE fh, BOOL inherit)
|
||||
{/*{{{*/
|
||||
static HANDLE dupHandle(HANDLE fh, BOOL inherit) {
|
||||
HANDLE copy, self = GetCurrentProcess();
|
||||
if (!DuplicateHandle(self, fh, self, ©, 0, inherit, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
|
||||
return NULL;
|
||||
}
|
||||
return copy;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
TSRM_API FILE *popen(const char *command, const char *type)
|
||||
{/*{{{*/
|
||||
{
|
||||
TSRMLS_FETCH();
|
||||
|
||||
return popen_ex(command, type, NULL, NULL);
|
||||
}/*}}}*/
|
||||
return popen_ex(command, type, NULL, NULL TSRMLS_CC);
|
||||
}
|
||||
|
||||
TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, const char *env)
|
||||
{/*{{{*/
|
||||
TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env TSRMLS_DC)
|
||||
{
|
||||
FILE *stream = NULL;
|
||||
int fno, type_len, read, mode;
|
||||
STARTUPINFOW startup;
|
||||
int fno, type_len = strlen(type), read, mode;
|
||||
STARTUPINFO startup;
|
||||
PROCESS_INFORMATION process;
|
||||
SECURITY_ATTRIBUTES security;
|
||||
HANDLE in, out;
|
||||
DWORD dwCreateFlags = 0;
|
||||
BOOL res;
|
||||
process_pair *proc;
|
||||
char *cmd = NULL;
|
||||
wchar_t *cmdw = NULL, *cwdw = NULL, *envw = NULL;
|
||||
char *cmd;
|
||||
int i;
|
||||
char *ptype = (char *)type;
|
||||
HANDLE thread_token = NULL;
|
||||
HANDLE token_user = NULL;
|
||||
@@ -465,38 +476,17 @@ TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
type_len = (int)strlen(type);
|
||||
if (type_len < 1 || type_len > 2) {
|
||||
/*The following two checks can be removed once we drop XP support */
|
||||
type_len = strlen(type);
|
||||
if (type_len <1 || type_len > 2) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ptype[0] != 'r' && ptype[0] != 'w') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (type_len > 1 && (ptype[1] != 'b' && ptype[1] != 't')) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /s /c ")+2);
|
||||
if (!cmd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sprintf(cmd, "%s /s /c \"%s\"", TWG(comspec), command);
|
||||
cmdw = php_win32_cp_any_to_w(cmd);
|
||||
if (!cmdw) {
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cwd) {
|
||||
cwdw = php_win32_ioutil_any_to_w(cwd);
|
||||
if (!cwdw) {
|
||||
free(cmd);
|
||||
free(cmdw);
|
||||
for (i=0; i < type_len; i++) {
|
||||
if (!(*ptype == 'r' || *ptype == 'w' || *ptype == 'b' || *ptype == 't')) {
|
||||
return NULL;
|
||||
}
|
||||
ptype++;
|
||||
}
|
||||
|
||||
security.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
@@ -504,16 +494,13 @@ TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd,
|
||||
security.lpSecurityDescriptor = NULL;
|
||||
|
||||
if (!type_len || !CreatePipe(&in, &out, &security, 2048L)) {
|
||||
free(cmdw);
|
||||
free(cwdw);
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&startup, 0, sizeof(STARTUPINFOW));
|
||||
memset(&startup, 0, sizeof(STARTUPINFO));
|
||||
memset(&process, 0, sizeof(PROCESS_INFORMATION));
|
||||
|
||||
startup.cb = sizeof(STARTUPINFOW);
|
||||
startup.cb = sizeof(STARTUPINFO);
|
||||
startup.dwFlags = STARTF_USESTDHANDLES;
|
||||
startup.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
@@ -545,35 +532,26 @@ TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd,
|
||||
}
|
||||
}
|
||||
|
||||
envw = php_win32_cp_env_any_to_w(env);
|
||||
if (envw) {
|
||||
dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
|
||||
} else {
|
||||
if (env) {
|
||||
free(cmd);
|
||||
free(cmdw);
|
||||
free(cwdw);
|
||||
return NULL;
|
||||
}
|
||||
cmd = (char*)malloc(strlen(command)+strlen(TWG(comspec))+sizeof(" /c ")+2);
|
||||
if (!cmd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sprintf(cmd, "%s /c \"%s\"", TWG(comspec), command);
|
||||
if (asuser) {
|
||||
res = CreateProcessAsUserW(token_user, NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
|
||||
res = CreateProcessAsUser(token_user, NULL, cmd, &security, &security, security.bInheritHandle, dwCreateFlags, env, cwd, &startup, &process);
|
||||
CloseHandle(token_user);
|
||||
} else {
|
||||
res = CreateProcessW(NULL, cmdw, &security, &security, security.bInheritHandle, dwCreateFlags, envw, cwdw, &startup, &process);
|
||||
res = CreateProcess(NULL, cmd, &security, &security, security.bInheritHandle, dwCreateFlags, env, cwd, &startup, &process);
|
||||
}
|
||||
free(cmd);
|
||||
free(cmdw);
|
||||
free(cwdw);
|
||||
free(envw);
|
||||
|
||||
if (!res) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CloseHandle(process.hThread);
|
||||
proc = process_get(NULL);
|
||||
proc = process_get(NULL TSRMLS_CC);
|
||||
|
||||
if (read) {
|
||||
fno = _open_osfhandle((tsrm_intptr_t)in, _O_RDONLY | mode);
|
||||
@@ -587,14 +565,15 @@ TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd,
|
||||
proc->prochnd = process.hProcess;
|
||||
proc->stream = stream;
|
||||
return stream;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
TSRM_API int pclose(FILE *stream)
|
||||
{/*{{{*/
|
||||
{
|
||||
DWORD termstat = 0;
|
||||
process_pair *process;
|
||||
TSRMLS_FETCH();
|
||||
|
||||
if ((process = process_get(stream)) == NULL) {
|
||||
if ((process = process_get(stream TSRMLS_CC)) == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -607,68 +586,46 @@ TSRM_API int pclose(FILE *stream)
|
||||
CloseHandle(process->prochnd);
|
||||
|
||||
return termstat;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
TSRM_API int shmget(key_t key, size_t size, int flags)
|
||||
{/*{{{*/
|
||||
TSRM_API int shmget(int key, int size, int flags)
|
||||
{
|
||||
shm_pair *shm;
|
||||
char shm_segment[26], shm_info[29];
|
||||
HANDLE shm_handle = NULL, info_handle = NULL;
|
||||
HANDLE shm_handle, info_handle;
|
||||
BOOL created = FALSE;
|
||||
|
||||
if (key != IPC_PRIVATE) {
|
||||
snprintf(shm_segment, sizeof(shm_segment), "TSRM_SHM_SEGMENT:%d", key);
|
||||
snprintf(shm_info, sizeof(shm_info), "TSRM_SHM_DESCRIPTOR:%d", key);
|
||||
|
||||
shm_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment);
|
||||
info_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_info);
|
||||
if (size < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!shm_handle && !info_handle) {
|
||||
sprintf(shm_segment, "TSRM_SHM_SEGMENT:%d", key);
|
||||
sprintf(shm_info, "TSRM_SHM_DESCRIPTOR:%d", key);
|
||||
|
||||
shm_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_segment);
|
||||
info_handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, shm_info);
|
||||
|
||||
if ((!shm_handle && !info_handle)) {
|
||||
if (flags & IPC_CREAT) {
|
||||
#if SIZEOF_SIZE_T == 8
|
||||
DWORD high = size >> 32;
|
||||
DWORD low = (DWORD)size;
|
||||
#else
|
||||
DWORD high = 0;
|
||||
DWORD low = size;
|
||||
#endif
|
||||
shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, high, low, key == IPC_PRIVATE ? NULL : shm_segment);
|
||||
info_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(shm->descriptor), key == IPC_PRIVATE ? NULL : shm_info);
|
||||
shm_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, shm_segment);
|
||||
info_handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(shm->descriptor), shm_info);
|
||||
created = TRUE;
|
||||
}
|
||||
if (!shm_handle || !info_handle) {
|
||||
if (shm_handle) {
|
||||
CloseHandle(shm_handle);
|
||||
}
|
||||
if (info_handle) {
|
||||
CloseHandle(info_handle);
|
||||
}
|
||||
if ((!shm_handle || !info_handle)) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (flags & IPC_EXCL) {
|
||||
if (shm_handle) {
|
||||
CloseHandle(shm_handle);
|
||||
}
|
||||
if (info_handle) {
|
||||
CloseHandle(info_handle);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
shm = shm_get(key, NULL);
|
||||
if (!shm) {
|
||||
CloseHandle(shm_handle);
|
||||
CloseHandle(info_handle);
|
||||
return -1;
|
||||
}
|
||||
shm->segment = shm_handle;
|
||||
shm->info = info_handle;
|
||||
shm->descriptor = MapViewOfFileEx(shm->info, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
|
||||
|
||||
if (NULL != shm->descriptor && created) {
|
||||
if (created) {
|
||||
shm->descriptor->shm_perm.key = key;
|
||||
shm->descriptor->shm_segsz = size;
|
||||
shm->descriptor->shm_ctime = time(NULL);
|
||||
@@ -682,45 +639,36 @@ TSRM_API int shmget(key_t key, size_t size, int flags)
|
||||
shm->descriptor->shm_perm.mode = shm->descriptor->shm_perm.seq = 0;
|
||||
}
|
||||
|
||||
if (NULL != shm->descriptor && (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz)) {
|
||||
if (NULL != shm->segment) {
|
||||
CloseHandle(shm->segment);
|
||||
}
|
||||
if (shm->descriptor->shm_perm.key != key || size > shm->descriptor->shm_segsz ) {
|
||||
CloseHandle(shm->segment);
|
||||
UnmapViewOfFile(shm->descriptor);
|
||||
CloseHandle(shm->info);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return key;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
TSRM_API void *shmat(int key, const void *shmaddr, int flags)
|
||||
{/*{{{*/
|
||||
{
|
||||
shm_pair *shm = shm_get(key, NULL);
|
||||
|
||||
if (!shm->segment) {
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
shm->addr = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
|
||||
|
||||
if (NULL == shm->addr) {
|
||||
int err = GetLastError();
|
||||
SET_ERRNO_FROM_WIN32_CODE(err);
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
shm->descriptor->shm_atime = time(NULL);
|
||||
shm->descriptor->shm_lpid = getpid();
|
||||
shm->descriptor->shm_nattch++;
|
||||
|
||||
shm->addr = MapViewOfFileEx(shm->segment, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
|
||||
|
||||
return shm->addr;
|
||||
}/*}}}*/
|
||||
}
|
||||
|
||||
TSRM_API int shmdt(const void *shmaddr)
|
||||
{/*{{{*/
|
||||
{
|
||||
shm_pair *shm = shm_get(0, (void*)shmaddr);
|
||||
int ret;
|
||||
|
||||
if (!shm->segment) {
|
||||
return -1;
|
||||
@@ -730,16 +678,10 @@ TSRM_API int shmdt(const void *shmaddr)
|
||||
shm->descriptor->shm_lpid = getpid();
|
||||
shm->descriptor->shm_nattch--;
|
||||
|
||||
ret = UnmapViewOfFile(shm->addr) ? 0 : -1;
|
||||
if (!ret && shm->descriptor->shm_nattch <= 0) {
|
||||
ret = UnmapViewOfFile(shm->descriptor) ? 0 : -1;
|
||||
shm->descriptor = NULL;
|
||||
}
|
||||
return ret;
|
||||
}/*}}}*/
|
||||
return UnmapViewOfFile(shm->addr) ? 0 : -1;
|
||||
}
|
||||
|
||||
TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf)
|
||||
{/*{{{*/
|
||||
TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf) {
|
||||
shm_pair *shm = shm_get(key, NULL);
|
||||
|
||||
if (!shm->segment) {
|
||||
@@ -767,59 +709,15 @@ TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf)
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}/*}}}*/
|
||||
|
||||
static zend_always_inline void UnixTimeToFileTime(time_t t, LPFILETIME pft) /* {{{ */
|
||||
{
|
||||
// Note that LONGLONG is a 64-bit value
|
||||
LONGLONG ll;
|
||||
|
||||
ll = t * 10000000LL + 116444736000000000LL;
|
||||
pft->dwLowDateTime = (DWORD)ll;
|
||||
pft->dwHighDateTime = ll >> 32;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
TSRM_API int win32_utime(const char *filename, struct utimbuf *buf) /* {{{ */
|
||||
TSRM_API char *realpath(char *orig_path, char *buffer)
|
||||
{
|
||||
FILETIME mtime, atime;
|
||||
HANDLE hFile;
|
||||
PHP_WIN32_IOUTIL_INIT_W(filename)
|
||||
|
||||
if (!pathw) {
|
||||
return -1;
|
||||
int ret = GetFullPathName(orig_path, _MAX_PATH, buffer, NULL);
|
||||
if(!ret || ret > _MAX_PATH) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hFile = CreateFileW(pathw, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, NULL,
|
||||
OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
|
||||
PHP_WIN32_IOUTIL_CLEANUP_W()
|
||||
|
||||
/* OPEN_ALWAYS mode sets the last error to ERROR_ALREADY_EXISTS but
|
||||
the CreateFile operation succeeds */
|
||||
if (GetLastError() == ERROR_ALREADY_EXISTS) {
|
||||
SetLastError(0);
|
||||
}
|
||||
|
||||
if ( hFile == INVALID_HANDLE_VALUE ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!buf) {
|
||||
SYSTEMTIME st;
|
||||
GetSystemTime(&st);
|
||||
SystemTimeToFileTime(&st, &mtime);
|
||||
atime = mtime;
|
||||
} else {
|
||||
UnixTimeToFileTime(buf->modtime, &mtime);
|
||||
UnixTimeToFileTime(buf->actime, &atime);
|
||||
}
|
||||
if (!SetFileTime(hFile, NULL, &atime, &mtime)) {
|
||||
CloseHandle(hFile);
|
||||
return -1;
|
||||
}
|
||||
CloseHandle(hFile);
|
||||
return 1;
|
||||
return buffer;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#endif
|
||||
+15
-12
@@ -1,11 +1,13 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) The PHP Group |
|
||||
| PHP Version 5 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2013 The PHP Group |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 3.01 of the PHP license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| 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. |
|
||||
@@ -14,16 +16,16 @@
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef TSRM_WIN32_H
|
||||
#define TSRM_WIN32_H
|
||||
|
||||
#include "TSRM.h"
|
||||
#include <windows.h>
|
||||
#include <sys/utime.h>
|
||||
#include "win32/ipc.h"
|
||||
|
||||
struct ipc_perm {
|
||||
key_t key;
|
||||
int key;
|
||||
unsigned short uid;
|
||||
unsigned short gid;
|
||||
unsigned short cuid;
|
||||
@@ -34,7 +36,7 @@ struct ipc_perm {
|
||||
|
||||
struct shmid_ds {
|
||||
struct ipc_perm shm_perm;
|
||||
size_t shm_segsz;
|
||||
int shm_segsz;
|
||||
time_t shm_atime;
|
||||
time_t shm_dtime;
|
||||
time_t shm_ctime;
|
||||
@@ -66,8 +68,7 @@ typedef struct {
|
||||
} tsrm_win32_globals;
|
||||
|
||||
#ifdef ZTS
|
||||
# define TWG(v) TSRMG_STATIC(win32_globals_id, tsrm_win32_globals *, v)
|
||||
TSRMLS_CACHE_EXTERN()
|
||||
# define TWG(v) TSRMG(win32_globals_id, tsrm_win32_globals *, v)
|
||||
#else
|
||||
# define TWG(v) (win32_globals.v)
|
||||
#endif
|
||||
@@ -89,19 +90,21 @@ TSRMLS_CACHE_EXTERN()
|
||||
#define SHM_RND FILE_MAP_WRITE
|
||||
#define SHM_REMAP FILE_MAP_COPY
|
||||
|
||||
const char * tsrm_win32_get_path_sid_key(const char *pathname, size_t pathname_len, size_t *key_len);
|
||||
char * tsrm_win32_get_path_sid_key(const char *pathname TSRMLS_DC);
|
||||
|
||||
TSRM_API void tsrm_win32_startup(void);
|
||||
TSRM_API void tsrm_win32_shutdown(void);
|
||||
|
||||
TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, const char *env);
|
||||
TSRM_API FILE *popen_ex(const char *command, const char *type, const char *cwd, char *env TSRMLS_DC);
|
||||
TSRM_API FILE *popen(const char *command, const char *type);
|
||||
TSRM_API int pclose(FILE *stream);
|
||||
TSRM_API int tsrm_win32_access(const char *pathname, int mode);
|
||||
TSRM_API int tsrm_win32_access(const char *pathname, int mode TSRMLS_DC);
|
||||
TSRM_API int win32_utime(const char *filename, struct utimbuf *buf);
|
||||
|
||||
TSRM_API int shmget(key_t key, size_t size, int flags);
|
||||
TSRM_API int shmget(int key, int size, int flags);
|
||||
TSRM_API void *shmat(int key, const void *shmaddr, int flags);
|
||||
TSRM_API int shmdt(const void *shmaddr);
|
||||
TSRM_API int shmctl(int key, int cmd, struct shmid_ds *buf);
|
||||
|
||||
TSRM_API char *realpath(char *orig_path, char *buffer);
|
||||
#endif
|
||||
@@ -1,444 +1,306 @@
|
||||
PHP 8.1 UPGRADE NOTES
|
||||
$Id$
|
||||
|
||||
PHP 5.5 UPGRADE NOTES
|
||||
|
||||
1. Backward Incompatible Changes
|
||||
2. New Features
|
||||
3. Changes in SAPI modules
|
||||
4. Deprecated Functionality
|
||||
5. Changed Functions
|
||||
6. New Functions
|
||||
7. New Classes and Interfaces
|
||||
8. Removed Extensions and SAPIs
|
||||
9. Other Changes to Extensions
|
||||
10. New Global Constants
|
||||
11. Changes to INI File Handling
|
||||
12. Windows Support
|
||||
13. Other Changes
|
||||
14. Performance Improvements
|
||||
2. Changes in SAPI modules
|
||||
3. Deprecated Functionality
|
||||
4. Changed Functions
|
||||
5. New Functions
|
||||
6. New Classes and Interfaces
|
||||
7. Removed Extensions
|
||||
8. Other Changes to Extensions
|
||||
9. New Global Constants
|
||||
10. Changes to INI File Handling
|
||||
11. Windows Support
|
||||
12. Other Changes
|
||||
|
||||
|
||||
========================================
|
||||
1. Backward Incompatible Changes
|
||||
========================================
|
||||
|
||||
- Drop Windows XP and 2003 support. (Pierre)
|
||||
- All internal case insensitivity handling for class, function and constant
|
||||
names is done according to ASCII rules, current locale settings are ignored.
|
||||
- php_logo_guid(), php_egg_logo_guid(), php_real_logo_guid() and
|
||||
zend_logo_guid() have been removed
|
||||
- Removal of Logo GUIDs
|
||||
|
||||
- extensions can't override zend_execute() any more, they should override
|
||||
zend_execute_ex() instead. The EG(current_execute_data) is already
|
||||
initialized in zend_execute_ex(), so for compatibility extensions
|
||||
may need to use EG(current_execute_data)->prev_execute_data instead.
|
||||
- removed EG(arg_types_stack), EX(fbc), EX(called_scope), EX(current_object)
|
||||
- added op_array->nested_calls. It's calculated at compile time.
|
||||
- added EX(call_slots). It is an array to store information about syntaticaly
|
||||
nested calls (e.g. foo(bar())). It's preallocated together with execute_data.
|
||||
- added EX(call) - pointer to a current calling function. Actually an
|
||||
element of EX(call_slots)
|
||||
- opcodes INIT_METHOD_CALL, ZEND_INIT_STATIC_METHOD_CALL,
|
||||
ZEND_INIT_FCALL_BY_NAME, ZEND_INIT_NS_FCALL_BY_NAME use result.num as
|
||||
an index in EX(call_slots)
|
||||
- opcode ZEND_NEW uses extended_vallue as an index in EX(call_slots)
|
||||
- opcoes ZEND_DO_FCALL and ZEND_DO_FCALL_BY_NAME use op2.num as
|
||||
an index in EX(call_slots)
|
||||
- added op_array->used_stack. It's calculated at compile time and the
|
||||
corresponding stack space is preallocated together with execute_data.
|
||||
ZEND_SEND* and ZEND_DO_FCALL* don't need to check for stack overflow
|
||||
anymore.
|
||||
- Removed execute_data->Ts field. The VM temporary variables always allocated
|
||||
immediately before execute_data structure. Now they are accessed by offset
|
||||
from the execute_data base pointer (instead of execute_data->Ts). Compiler
|
||||
stores new offsets in op_array->opcodes[*].op?.num. You can use macros
|
||||
EX_TMP_VAR() and EX_TMP_VAR_NUM() to access temp_variable by offset or
|
||||
number. You can convert number to offset using EX_TMP_VAR_NUM(0, num) or
|
||||
offset to number (EX_TMP_VAR_NUM(0,0)-EX_TMP_VAR(0,offset)).
|
||||
- Removed execute_data->CVs field. The VM compiled variables always allocated
|
||||
immediately after execute_data structure. Now they are accessed by offset
|
||||
from the execute_data base pointer (instead of execute_data->CVs). You can
|
||||
use macros EX_CV_NUM() to access compiled variables by number.
|
||||
|
||||
========================================
|
||||
2. New Features
|
||||
========================================
|
||||
|
||||
- Support list in foreach. (Laruence)
|
||||
(wiki.php.net/rfc/foreachlist)
|
||||
- Support finally keyword. (Laruence)
|
||||
(wiki.php.net/rfc/finally)
|
||||
- Support constant array/string dereferencing. (Laruence)
|
||||
(https://wiki.php.net/rfc/constdereference)
|
||||
- Add support for using empty() on the result of function calls and
|
||||
other expressions. Thus it is now possible to write empty(getArray()),
|
||||
for example. (https://wiki.php.net/rfc/empty_isset_exprs)
|
||||
- Added generators.
|
||||
(https://wiki.php.net/rfc/generators)
|
||||
|
||||
========================================
|
||||
2. Changes in SAPI modules
|
||||
========================================
|
||||
|
||||
|
||||
========================================
|
||||
3. Deprecated Functionality
|
||||
========================================
|
||||
|
||||
- The original MySQL extension is now deprecated, and will generate deprecation
|
||||
warnings when connecting to a database through mysql_connect(),
|
||||
mysql_pconnect() or by establishing an implicit connection. Use MySQLi or PDO
|
||||
instead.
|
||||
- The preg_replace /e modifier is now deprecated. Use
|
||||
preg_replace_callback instead.
|
||||
(https://wiki.php.net/rfc/remove_preg_replace_eval_modifier)
|
||||
- IntlDateFormatter::setTimeZoneID() and datefmt_set_timezone_id() are
|
||||
deprecated. Use IntlDateFormatter::setTimeZone() or datefmt_set_timezone()
|
||||
instead.
|
||||
- mcrypt_ecb(), mcrypt_cbc(), mcrypt_cfb() and mcrypt_ofb() now throw
|
||||
E_DEPRECATED. Their use was already previously discouraged in the docs,
|
||||
but that predated the existence of E_DEPRECATED.
|
||||
|
||||
========================================
|
||||
4. Changed Functions
|
||||
========================================
|
||||
|
||||
- pack()/unpack() had the following changes, which bring it more in line
|
||||
with Perl's behavior:
|
||||
- Implemented format character "Z": NUL-padded string
|
||||
- "a" now does not remove trailing NUL characters on unpack() anymore
|
||||
- "A" will now strip all trailing ASCII whitespace on unpack() (it used to
|
||||
remove only trailing spaces).
|
||||
- MessageFormatter::format() and related functions now accepted named arguments
|
||||
and mixed numeric/named arguments in ICU 4.8+.
|
||||
- MessageFormatter::format() and related functions now don't error out when
|
||||
an insufficient argument count is provided. Instead, the placeholders will
|
||||
remain unsubstituted.
|
||||
- MessageFormatter::parse() and MessageFormat::format() (and their static
|
||||
equivalents) now don't throw away better than second precision in the
|
||||
arguments.
|
||||
- IntlDateFormatter::__construct and datefmt_create() now accept for the
|
||||
$timezone argument time zone identifiers, IntlTimeZone objects, DateTimeZone
|
||||
objects and NULL. It used to accept only time zone identifiers and NULL.
|
||||
Invalid time zone identifiers are no longer accepted. Emptry strings are
|
||||
no longer accepted.
|
||||
- The default time zone used in IntlDateFormatter::__construct and
|
||||
datefmt_create() (when the corresponding argument is not passed or NULL is
|
||||
passed) is now the one given by date_default_timezone_get(), not the
|
||||
default ICU time zone.
|
||||
- The time zone passed to the IntlDateFormatter is ignored if it is NULL and if
|
||||
the calendar passed is an IntlCalendar object -- in this case, the
|
||||
IntlCalendar's time zone will be used instead. Otherwise, the time zone
|
||||
specified in the $timezone argument is used instead. This does not affect
|
||||
old code, as IntlCalendar was introduced in this version.
|
||||
- IntlDateFormatter::__construct and datefmt_create() now accept for the
|
||||
$calendar argument also IntlCalendar objects.
|
||||
- IntlDateFormatter::getCalendar() and datefmt_get_calendar() return false
|
||||
if the IntlDateFormatter was set up with an IntlCalendar instead of the
|
||||
constants IntlDateFormatter::GREGORIAN/TRADITIONAL. IntlCalendar did not
|
||||
exist before this version.
|
||||
- IntlDateFormatter::setCalendar() and datefmt_set_calendar() now also accept
|
||||
an IntlCalendar object, in which case its time zone is taken. Passing a
|
||||
constant is still allowed, and still keeps the time zone.
|
||||
- IntlDateFormatter::format() and datefmt_format() now also accept an
|
||||
IntlCalendar object for formatting.
|
||||
- php_logo_guid(), php_egg_logo_guid(), php_real_logo_guid() and
|
||||
zend_logo_guid() have been removed
|
||||
- set_error_handler(NULL) can now be used to reset the error handler.
|
||||
Furthermore both set_error_handler(NULL) and set_exception_handler(NULL) will
|
||||
now return the previously defined error/exception handler. Previously
|
||||
bool(true) was returned.
|
||||
|
||||
========================================
|
||||
5. New Functions
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Access to the $GLOBALS array is now subject to a number of restrictions.
|
||||
Read and write access to individual array elements like $GLOBALS['var']
|
||||
continues to work as-is. Read-only access to the entire $GLOBALS array also
|
||||
continues to be supported. However, write access to the entire $GLOBALS
|
||||
array is no longer supported. For example, array_pop($GLOBALS) will result
|
||||
in an error.
|
||||
RFC: https://wiki.php.net/rfc/restrict_globals_usage
|
||||
. Passing null to a non-nullable argument of a built-in function is
|
||||
deprecated. This matches the behavior of user-defined functions, where null
|
||||
is never accepted by non-nullable arguments.
|
||||
user-defined functions.
|
||||
- boolval()
|
||||
- password_get_info()
|
||||
- password_hash()
|
||||
- password_needs_rehash()
|
||||
- password_verify()
|
||||
|
||||
var_dump(str_contains("foobar", null));
|
||||
// Deprecated: Passing null to parameter #2 ($needle) of type string
|
||||
// is deprecated
|
||||
- Hash:
|
||||
- hash_pbkdf2()
|
||||
|
||||
RFC: https://wiki.php.net/rfc/deprecate_null_to_scalar_internal_arg
|
||||
. When a method using static variables is inherited (but not overridden), the
|
||||
inherited method will now share static variables with the parent method.
|
||||
- Intl:
|
||||
- datefmt_format_object()
|
||||
- datefmt_get_calendar_object()
|
||||
- datefmt_get_timezone()
|
||||
- datefmt_set_timezone()
|
||||
- datefmt_get_calendar_object()
|
||||
- intlcal_create_instance()
|
||||
- intlcal_get_keyword_values_for_locale()
|
||||
- intlcal_get_now()
|
||||
- intlcal_get_available_locales()
|
||||
- intlcal_get()
|
||||
- intlcal_get_time()
|
||||
- intlcal_set_time()
|
||||
- intlcal_add()
|
||||
- intlcal_set_time_zone()
|
||||
- intlcal_after()
|
||||
- intlcal_before()
|
||||
- intlcal_set()
|
||||
- intlcal_roll()
|
||||
- intlcal_clear()
|
||||
- intlcal_field_difference()
|
||||
- intlcal_get_actual_maximum()
|
||||
- intlcal_get_actual_minimum()
|
||||
- intlcal_get_day_of_week_type()
|
||||
- intlcal_get_first_day_of_week()
|
||||
- intlcal_get_greatest_minimum()
|
||||
- intlcal_get_least_maximum()
|
||||
- intlcal_get_locale()
|
||||
- intlcal_get_maximum()
|
||||
- intlcal_get_minimal_days_in_first_week()
|
||||
- intlcal_get_minimum()
|
||||
- intlcal_get_time_zone()
|
||||
- intlcal_get_type()
|
||||
- intlcal_get_weekend_transition()
|
||||
- intlcal_in_daylight_time()
|
||||
- intlcal_is_equivalent_to()
|
||||
- intlcal_is_lenient()
|
||||
- intlcal_is_set()
|
||||
- intlcal_is_weekend()
|
||||
- intlcal_set_first_day_of_week()
|
||||
- intlcal_set_lenient()
|
||||
- intlcal_equals()
|
||||
- intlcal_get_repeated_wall_time_option()
|
||||
- intlcal_get_skipped_wall_time_option()
|
||||
- intlcal_set_repeated_wall_time_option()
|
||||
- intlcal_set_skipped_wall_time_option()
|
||||
- intlcal_from_date_time()
|
||||
- intlcal_to_date_time()
|
||||
- intlcal_get_error_code()
|
||||
- intlcal_get_error_message()
|
||||
- intlgregcal_create_instance()
|
||||
- intlgregcal_set_gregorian_change()
|
||||
- intlgregcal_get_gregorian_change()
|
||||
- intlgregcal_is_leap_year()
|
||||
- intltz_create_time_zone()
|
||||
- intltz_create_default()
|
||||
- intltz_get_id()
|
||||
- intltz_get_gmt()
|
||||
- intltz_get_unknown()
|
||||
- intltz_create_enumeration()
|
||||
- intltz_count_equivalent_ids()
|
||||
- intltz_create_time_zone_id_enumeration()
|
||||
- intltz_get_canonical_id()
|
||||
- intltz_get_region()
|
||||
- intltz_get_tz_data_version()
|
||||
- intltz_get_equivalent_id()
|
||||
- intltz_use_daylight_time()
|
||||
- intltz_get_offset()
|
||||
- intltz_get_raw_offset()
|
||||
- intltz_has_same_rules()
|
||||
- intltz_get_display_name()
|
||||
- intltz_get_dst_savings()
|
||||
- intltz_from_date_time_zone()
|
||||
- intltz_to_date_time_zone()
|
||||
- intltz_get_error_code()
|
||||
- intltz_get_error_message()
|
||||
|
||||
class A {
|
||||
public static function counter() {
|
||||
static $counter = 0;
|
||||
$counter++;
|
||||
return $counter;
|
||||
}
|
||||
}
|
||||
class B extends A {}
|
||||
|
||||
var_dump(A::counter()); // int(1)
|
||||
var_dump(A::counter()); // int(2)
|
||||
var_dump(B::counter()); // int(3), previously int(1)
|
||||
var_dump(B::counter()); // int(4), previously int(2)
|
||||
|
||||
This means that static variables in methods now behave the same way as
|
||||
static properties.
|
||||
RFC: https://wiki.php.net/rfc/static_variable_inheritance
|
||||
. Most non-final internal methods now require overriding methods to declare a
|
||||
compatible return type, otherwise a deprecated notice is emitted during
|
||||
inheritance validation.
|
||||
RFC: https://wiki.php.net/rfc/internal_method_return_types
|
||||
|
||||
- Fileinfo:
|
||||
. The fileinfo functions now accept and return, respectively, finfo objects
|
||||
instead of resources.
|
||||
|
||||
- FTP:
|
||||
. The FTP functions now accept and return, respectively, FTP\Connection objects
|
||||
instead of resources.
|
||||
|
||||
- IMAP:
|
||||
. The IMAP functions now accept and return, respectively, IMAP\Connection objects
|
||||
instead of resources.
|
||||
|
||||
- LDAP:
|
||||
. The LDAP functions now accept and return, respectively, LDAP\Connection objects
|
||||
instead of "ldap link" resources. Return value checks using is_resource()
|
||||
should be replaced with checks for `false`.
|
||||
. The LDAP functions now accept and return, respectively, LDAP\Result objects
|
||||
instead of "ldap result" resources. Return value checks using is_resource()
|
||||
should be replaced with checks for `false`.
|
||||
. The LDAP functions now accept and return, respectively, LDAP\ResultEntry
|
||||
objects instead of "ldap result entry" resources. Return value checks using
|
||||
is_resource() should be replaced with checks for `false`.
|
||||
|
||||
- MySQLi:
|
||||
. mysqli_fetch_fields() and mysqli_fetch_field_direct() will now always return
|
||||
zero for max_length. You can compute this information by iterating over the
|
||||
result set and taking the maximum length. This is what PHP was doing
|
||||
internally previously.
|
||||
. The MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH option no longer has an effect.
|
||||
. The MYSQLI_STORE_RESULT_COPY_DATA option no longer has an effect.
|
||||
. The default error handling mode has been changed from "silent" to
|
||||
"exceptions". See https://www.php.net/manual/en/mysqli-driver.report-mode.php
|
||||
for details of behavior changes and how to explicitly set this attribute. To
|
||||
keep the old behavior, use mysqli_report(MYSQLI_REPORT_OFF);
|
||||
RFC: https://wiki.php.net/rfc/mysqli_default_errmode
|
||||
. Classes extending mysqli_stmt::execute() will be required to specify the
|
||||
additional parameter now.
|
||||
RFC: https://wiki.php.net/rfc/mysqli_bind_in_execute
|
||||
. mysqli::connect() will now return true instead of null on success.
|
||||
|
||||
- MySQLnd:
|
||||
. The mysqlnd.fetch_copy_data ini setting has been removed. However, this
|
||||
should not result in user-visible behavior changes.
|
||||
|
||||
- PDO:
|
||||
. PDO::ATTR_STRINGIFY_FETCHES now also stringifies values of type bool to
|
||||
"0" or "1". Previously booleans were not stringified.
|
||||
. Calling bindColumn() with PDO::PARAM_LOB (and assuming stringification is
|
||||
not enabled) will now consistently bind a stream result, as documented.
|
||||
Previously the result would be either a stream or a string depending on the
|
||||
used database driver and the time the binding is performed.
|
||||
|
||||
- PDO MySQL:
|
||||
. Integers and floats in result sets will now be returned using native PHP
|
||||
types instead of strings when using emulated prepared statements. This
|
||||
matches the behavior of native prepared statements. You can restore the
|
||||
previous behavior by enabling the PDO::ATTR_STRINGIFY_FETCHES option.
|
||||
|
||||
- PDO SQLite:
|
||||
. Integers and floats in results sets will now be returned using native PHP
|
||||
types. You can restore the previous behavior by enabling the
|
||||
PDO::ATTR_STRINGIFY_FETCHES option.
|
||||
|
||||
- PgSQL:
|
||||
. The PgSQL functions now accept and return, respectively, \PgSql\Connection
|
||||
objects instead of "pgsql link" resources. Return value checks using
|
||||
is_resource() should be replaced with checks for `false`.
|
||||
. The PgSQL functions now accept and return, respectively, \PgSql\Result
|
||||
objects instead of "pgsql result" resources. Return value checks using
|
||||
is_resource() should be replaced with checks for `false`.
|
||||
. The PgSQL functions now accept and return, respectively, \PgSql\Lob
|
||||
objects instead of "pgsql large object" resources. Return value checks
|
||||
using is_resource() should be replaced with checks for `false`.
|
||||
|
||||
- PSpell:
|
||||
. The PSpell functions now accept and return, respectively, PSpell\Dictionary objects
|
||||
instead of "pspell" resources. Return value checks using is_resource()
|
||||
should be replaced with checks for `false`.
|
||||
. The PSpell functions now accept and return, respectively, PSpell\Config objects
|
||||
instead of "pspell config" resources. Return value checks using is_resource()
|
||||
should be replaced with checks for `false`.
|
||||
|
||||
- Standard:
|
||||
. version_compare() no longer accepts undocumented operator abbreviations.
|
||||
. htmlspecialchars(), htmlentities(), htmlspecialchars_decode(),
|
||||
html_entity_decode() and get_html_translation_table() now use
|
||||
ENT_QUOTES | ENT_SUBSTITUTE rather than ENT_COMPAT by default. This means
|
||||
that ' is escaped to ' while previously it was left alone.
|
||||
Additionally, malformed UTF-8 will be replaced by a Unicode substitution
|
||||
character, instead of resulting in an empty string.
|
||||
. debug_zval_dump() will now print reference wrappers with their refcount,
|
||||
instead of only prepending a "&" to the value. This more accurately models
|
||||
reference representation since PHP 7.0.
|
||||
. debug_zval_dump() will not print "interned" instead of a dummy refcount of
|
||||
one for interned strings and immutable arrays.
|
||||
. fputcsv() now accepts a new "eol" argument which allow to define a custom
|
||||
eol sequence, the default remains the same and is "\n".
|
||||
- IntlDateFormatter::formatObject()
|
||||
- IntlDateFormatter::getCalendarObject()
|
||||
- IntlDateFormatter::getTimeZone()
|
||||
- IntlDateFormatter::setTimeZone()
|
||||
|
||||
- SPL:
|
||||
. SplFileObject::fputcsv() now accepts a new "eol" argument which allow to
|
||||
define a custom eol sequence, the default remains the same and is "\n".
|
||||
- SplFixedArray::__wakeup()
|
||||
|
||||
========================================
|
||||
2. New Features
|
||||
6. New Classes and Interfaces
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. It is now possible to specify octal integer by using the explicit "0o"/"0O"
|
||||
prefix similar to hexadecimal ("0x"/"0X) and binary ("0b"/"0B") integer
|
||||
literals.
|
||||
RFC: https://wiki.php.net/rfc/explicit_octal_notation
|
||||
. Added support for array unpacking with strings keys.
|
||||
RFC: https://wiki.php.net/rfc/array_unpacking_string_keys
|
||||
. Added support for enumerations.
|
||||
RFC: https://wiki.php.net/rfc/enumerations
|
||||
. Added support for never return type
|
||||
RFC: https://wiki.php.net/rfc/noreturn_type
|
||||
. Added support for fibers.
|
||||
RFC: https://wiki.php.net/rfc/fibers
|
||||
. File uploads now provide an additional full_path key, which contains the
|
||||
full path (rather than just the basename) of the uploaded file. This is
|
||||
intended for use in conjunction with "upload webkitdirectory".
|
||||
|
||||
- Curl:
|
||||
. Added CURLOPT_DOH_URL option.
|
||||
. Added CURLStringFile, which can be used to post a file from a string rather
|
||||
than a file:
|
||||
|
||||
$file = new CURLStringFile($data, 'filename.txt', 'text/plain');
|
||||
curl_setopt($curl, CURLOPT_POSTFIELDS, ['file' => $file]);
|
||||
|
||||
- FPM:
|
||||
. Added openmetrics status format. It can be used by Prometheus to fetch FPM
|
||||
metrics.
|
||||
. Added new pool option for the dynamic process manager called
|
||||
pm.max_spawn_rate. It allows to start number of children in a faster rate
|
||||
when dynamic pm is selected. The default value is 32 which was the previous
|
||||
hard coded value.
|
||||
|
||||
- hash:
|
||||
. The following functions have changed signatures:
|
||||
|
||||
- function hash(string $algo, string $data, bool $binary = false, array $options = []): string|false {}
|
||||
- function hash_file(string $algo, string $filename, bool $binary = false, array $options = []): string|false {}
|
||||
- function hash_init(string $algo, int $flags = 0, string $key = "", array $options = []): HashContext {}
|
||||
|
||||
The additional `$options` argument can be used to pass algorithm specific data.
|
||||
|
||||
. Added MurmurHash3 with streaming support. The following variants are implemented:
|
||||
|
||||
- murmur3a, 32-bit hash
|
||||
- murmur3c, 128-bit hash for x86
|
||||
- murmur3f, 128-bit hash for x64
|
||||
|
||||
The initial hash state can be passed through the `seed` key in the `$options` array, for example:
|
||||
|
||||
```php
|
||||
$h = hash("murmur3f", $data, options: ["seed" => 42]);
|
||||
echo $h, "\n";
|
||||
```
|
||||
|
||||
A valid seed value is within the range from 0 to the platform defined UINT_MAX, usually 4294967295.
|
||||
|
||||
. Added xxHash. The implementation brings in the following arguments
|
||||
|
||||
- xxh32, 32-bit hash
|
||||
- xxh64, 64-bit hash
|
||||
- xxh3, 64-bit hash
|
||||
- xxh128, 128-bit hash
|
||||
|
||||
The initial hash state can be passed through the `seed` key in the `$options` array, for example:
|
||||
|
||||
```php
|
||||
$h = hash("xxh3", $data, options: ["seed" => 42]);
|
||||
echo $h, "\n";
|
||||
```
|
||||
|
||||
Secret usage is supported through passing the `secret` key in the `$options` array, too:
|
||||
|
||||
```php
|
||||
$h = hash("xxh3", $data, options: ["secret" => "at least 136 bytes long secret here"]);
|
||||
echo $h, "\n";
|
||||
```
|
||||
|
||||
Note, that the quality of the custom secret is crucial for the quality of the resulting hash. It is
|
||||
highly recommended for the secret to use the best possible entropy.
|
||||
|
||||
- MySQLi:
|
||||
. The mysqli.local_infile_directory ini setting has been added, which can be
|
||||
used to specify a directory from which files are allowed to be loaded. It
|
||||
is only meaningful if mysqli.allow_local_infile is not enabled, as all
|
||||
directories are allowed in that case.
|
||||
. Binding in execute has been added to mysqli prepared statements.
|
||||
Parameters can now be passed to mysqli_stmt::execute as an array.
|
||||
RFC: https://wiki.php.net/rfc/mysqli_bind_in_execute
|
||||
. A new function has been added to mysqli_result called mysqli_fetch_column().
|
||||
It allows for fetching single scalar values from the result set.
|
||||
RFC: https://wiki.php.net/rfc/mysqli_fetch_column
|
||||
|
||||
- PDO MySQL:
|
||||
. The PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY attribute has been added, which
|
||||
can be used to specify a directory from which files are allowed to be
|
||||
loaded. It is only meaningful if PDO::MYSQL_ATTR_LOCAL_INFILE is not
|
||||
enabled, as all directories are allowed in that case.
|
||||
|
||||
- PDO SQLite:
|
||||
. SQLite's "file:" DSN syntax is now supported, which allows specifying
|
||||
additional flags. This feature is not available if open_basedir is set.
|
||||
Example:
|
||||
|
||||
new PDO('sqlite:file:path/to/sqlite.db?mode=ro')
|
||||
|
||||
- Posix:
|
||||
. Added POSIX_RLIMIT_KQUEUES and POSIX_RLIMIT_NPTS. These rlimits are only
|
||||
available on FreeBSD.
|
||||
- Intl:
|
||||
- IntlCalendar
|
||||
- IntlGregorianCalendar
|
||||
- IntlTimeZone
|
||||
- IntlBreakIterator
|
||||
- IntlRuleBasedBreakIterator
|
||||
- IntlCodePointBreakIterator
|
||||
|
||||
========================================
|
||||
3. Changes in SAPI modules
|
||||
7. Removed Extensions
|
||||
========================================
|
||||
|
||||
- CLI:
|
||||
. Using -a without the readline extension will now result in an error.
|
||||
Previously, -a without readline had the same behavior as calling php without
|
||||
any arguments, apart from printing an additional "Interactive mode enabled"
|
||||
message. This mode was not, in fact, interactive.
|
||||
|
||||
========================================
|
||||
4. Deprecated Functionality
|
||||
8. Other Changes to Extensions
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Implementing the Serializable interface without also implementing
|
||||
__serialize() and __unserialize() has been deprecated. You should either
|
||||
implement the new methods (if you only support PHP 7.4 and higher) or
|
||||
implement both (if you support older PHP versions as well).
|
||||
RFC: https://wiki.php.net/rfc/phase_out_serializable
|
||||
|
||||
- MySQLi:
|
||||
. The mysqli_driver::$driver_version property has been deprecated. The driver
|
||||
version is meaningless as it hasn't been updated in more than a decade. Use
|
||||
PHP_VERSION_ID instead.
|
||||
. Calling mysqli::get_client_info in OO style or passing $mysqli argument to
|
||||
mysqli_get_client_info() function has been deprecated. Use
|
||||
mysqli_get_client_info() without any arguments to obtain the client
|
||||
library version information.
|
||||
|
||||
- PDO:
|
||||
. The PDO::FETCH_SERIALIZE mode has been deprecated.
|
||||
RFC: https://wiki.php.net/rfc/phase_out_serializable
|
||||
- Intl:
|
||||
- This extension now requires ICU 4.0+.
|
||||
|
||||
========================================
|
||||
5. Changed Functions
|
||||
9. New Global Constants
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Properties order used in foreach, var_dump(), serialize(), object comparison
|
||||
etc. was changed. Now properties are naturally ordered according to their
|
||||
declaration and inheritance. Properties declared in a base class are going
|
||||
to be before the child properties. This order is consistent with internal
|
||||
layout of properties in zend_object structure and repeats the order in
|
||||
default_properties_table[] and properties_info_table[]. The old order was
|
||||
not documented and was caused by class inheritance implementation details.
|
||||
|
||||
- Filter:
|
||||
. The FILTER_FLAG_ALLOW_OCTAL flag of the FILTER_VALIDATE_INT filter now accept
|
||||
octal string with the leading octal prefix ("0o"/"0O")
|
||||
RFC: https://wiki.php.net/rfc/explicit_octal_notation
|
||||
|
||||
- GMP:
|
||||
. All GMP function now accept octal string with the leading octal prefix ("0o"/"0O")
|
||||
RFC: https://wiki.php.net/rfc/explicit_octal_notation
|
||||
|
||||
- PDO ODBC:
|
||||
. PDO::getAttributes() with PDO::ATTR_SERVER_INFO and PDO::ATTR_SERVER_VERSION
|
||||
now return values instead of throwing PDOException.
|
||||
- mysqli:
|
||||
- Added MYSQLI_SERVER_PUBLIC_KEY constant to be used with mysqli_options()
|
||||
|
||||
========================================
|
||||
6. New Functions
|
||||
10. Changes to INI File Handling
|
||||
========================================
|
||||
|
||||
- Core:
|
||||
. Added array_is_list(array $array), which will return true if the array keys are 0 .. count($array)-1 in that order.
|
||||
RFC: https://wiki.php.net/rfc/is_list
|
||||
- Intl:
|
||||
- Added intl.use_exceptions INI directive, which controls what happens when
|
||||
global errors are set together with intl.error_level.
|
||||
|
||||
- pcntl:
|
||||
. Added pcntl_rfork for FreeBSD variants
|
||||
|
||||
- Reflection:
|
||||
. Added ReflectionFunctionAbstract::getClosureUsedVariables
|
||||
|
||||
- Standard:
|
||||
. Added fsync() and fdatasync(), which instruct the operating system to
|
||||
flush its buffers to physical storage.
|
||||
RFC: https://wiki.php.net/rfc/fsync_function
|
||||
|
||||
- Sodium:
|
||||
. Added the XChaCha20 stream cipher interface functions:
|
||||
|
||||
- sodium_crypto_stream_xchacha20()
|
||||
- sodium_crypto_stream_xchacha20_keygen()
|
||||
- sodium_crypto_stream_xchacha20_xor()
|
||||
|
||||
. Added the Ristretto255 functions, which are available in libsodium 1.0.18.
|
||||
|
||||
Ristretto is a technique for constructing prime order elliptic curve groups with non-malleable encodings.
|
||||
Ristretto255 implements Ristretto atop Curve25519.
|
||||
|
||||
- sodium_crypto_core_ristretto255_add()
|
||||
- sodium_crypto_core_ristretto255_from_hash()
|
||||
- sodium_crypto_core_ristretto255_is_valid_point()
|
||||
- sodium_crypto_core_ristretto255_random()
|
||||
- sodium_crypto_core_ristretto255_scalar_add()
|
||||
- sodium_crypto_core_ristretto255_scalar_complement()
|
||||
- sodium_crypto_core_ristretto255_scalar_invert()
|
||||
- sodium_crypto_core_ristretto255_scalar_mul()
|
||||
- sodium_crypto_core_ristretto255_scalar_negate()
|
||||
- sodium_crypto_core_ristretto255_scalar_random()
|
||||
- sodium_crypto_core_ristretto255_scalar_reduce()
|
||||
- sodium_crypto_core_ristretto255_scalar_sub()
|
||||
- sodium_crypto_core_ristretto255_sub()
|
||||
- sodium_crypto_scalarmult_ristretto255()
|
||||
- sodium_crypto_scalarmult_ristretto255_base()
|
||||
- mysqlnd:
|
||||
- Added mysqlnd.sha256_server_public_key INI PERDIR setting that affects all
|
||||
APIs which use(are built) for mysqlnd. This allows ext/mysqli to be used
|
||||
with the new auth protocol, although at coarser level.
|
||||
|
||||
========================================
|
||||
7. New Classes and Interfaces
|
||||
11. Windows Support
|
||||
========================================
|
||||
|
||||
========================================
|
||||
8. Removed Extensions and SAPIs
|
||||
========================================
|
||||
- Apache 2.4 handler is supported as of PHP 5.5.0
|
||||
|
||||
|
||||
========================================
|
||||
9. Other Changes to Extensions
|
||||
12. Other Changes
|
||||
========================================
|
||||
|
||||
- MySQLi:
|
||||
. The mysqli_stmt::next_result() and mysqli::fetch_all() methods are now
|
||||
available when linking against libmysqlclient.
|
||||
|
||||
- OpenSSL:
|
||||
. The OpenSSL extension now requires at least OpenSSL version 1.0.2.
|
||||
|
||||
- Sockets:
|
||||
. FreeBSD defines SO_ACCEPTFILTER
|
||||
|
||||
- Standard:
|
||||
. --with-password-argon2 now uses pkg-config to detect libargon2. As such,
|
||||
an alternative libargon2 location should now be specified using
|
||||
PKG_CONFIG_PATH.
|
||||
|
||||
========================================
|
||||
10. New Global Constants
|
||||
========================================
|
||||
|
||||
- MySQLi:
|
||||
. MYSQLI_REFRESH_REPLICA has been added as a replacement for
|
||||
MYSQLI_REFRESH_SLAVE, in line with an upstream change in MySQL. The old
|
||||
constant is still available for backwards-compatibility reasons, but may
|
||||
be deprecated/removed in the future.
|
||||
|
||||
========================================
|
||||
11. Changes to INI File Handling
|
||||
========================================
|
||||
|
||||
- The log_errors_max_len ini setting has been removed. It no longer had an
|
||||
effect since PHP 8.0.
|
||||
|
||||
========================================
|
||||
12. Windows Support
|
||||
========================================
|
||||
|
||||
. The macro IGNORE_URL_WIN has been removed; it had no effect as of PHP 5.0.0.
|
||||
|
||||
========================================
|
||||
13. Other Changes
|
||||
========================================
|
||||
|
||||
========================================
|
||||
14. Performance Improvements
|
||||
========================================
|
||||
- Logo GUIDs will no longer work
|
||||
+50
-95
@@ -1,110 +1,65 @@
|
||||
PHP 8.1 INTERNALS UPGRADE NOTES
|
||||
$Id$
|
||||
|
||||
UPGRADE NOTES - PHP X.Y
|
||||
|
||||
1. Internal API changes
|
||||
a. Removed Zend APIs
|
||||
b. Zend Stream API
|
||||
c. zend_get_opcode_id()
|
||||
a. Streams pooling API
|
||||
b. Lowercasing and locales
|
||||
|
||||
2. Build system changes
|
||||
a. New compiler flags
|
||||
a. Unix build system changes
|
||||
b. Windows build system changes
|
||||
|
||||
3. Module changes
|
||||
a. ext/hash
|
||||
b. ext/pdo
|
||||
c. ext/standard
|
||||
d. ext/pgsql
|
||||
e. ext/pcre
|
||||
|
||||
========================
|
||||
1. Internal API changes
|
||||
========================
|
||||
a. The following APIs have been removed from the Zend Engine:
|
||||
- The spl_ce_Aggregate, spl_ce_ArrayAccess, spl_ce_Countable, spl_ce_Iterator, spl_ce_Serializable,
|
||||
spl_ce_Stringable, spl_ce_Traversable alias class entries have been removed in favor of zend_ce_aggregate,
|
||||
zend_ce_arrayaccess, zend_ce_countable, zend_ce_iterator, zend_ce_serializable, zend_ce_stringable,
|
||||
zend_ce_traversable.
|
||||
b. Zend Stream API has been changed to use "zend_string*" instead of "char*"
|
||||
- zend_file_handle.filename now is zend_string*
|
||||
- zend_file_handle.free_filename is removed. Now zend_file_handle.filename is always released.
|
||||
- added zend_file_handle.primary_script flag. SAPIs should set it for main executed script.
|
||||
- added zend_file_handle.in_list flag, which is set when a file_handle is added into CG(open_files)
|
||||
- added zend_stream_init_filename_ex() function, that takes filename as zend_string*
|
||||
- the "filename" parameter of functons zend_stream_open(), php_stream_open_for_zend_ex() and
|
||||
callback zend_stream_open_function() has been removed (it's now passed as a "filename" field of the
|
||||
file_handle parameter)
|
||||
- in zend_fopen() and zend_resolve_path() callbacks filename now passed as zend_string*
|
||||
- file_handles should be destroyed by zend_destroy_file_handle() function (usually in the same function
|
||||
the same function where they were created by zend_stream_init_*()). Previously there were two different
|
||||
destructors zend_destroy_file_handle() and zend_file_handle_dtor().
|
||||
- zend_ini_scanner_globals.filename now is zend_string*
|
||||
c. Added the zend_get_opcode_id() function, which is intended to get opcode id from name.
|
||||
|
||||
a. Streams pooling API
|
||||
|
||||
The streams pooling API has been removed. The following functions no longer
|
||||
exist:
|
||||
|
||||
PHPAPI int php_stream_context_get_link(php_stream_context *context,
|
||||
const char *hostent, php_stream **stream);
|
||||
PHPAPI int php_stream_context_set_link(php_stream_context *context,
|
||||
const char *hostent, php_stream *stream);
|
||||
PHPAPI int php_stream_context_del_link(php_stream_context *context,
|
||||
php_stream *stream);
|
||||
|
||||
b. Lowercasing and locales
|
||||
|
||||
The lowercasing functions in zend_operators.c were split into those that do
|
||||
lowercasing according to locale rules and those that do ASCII lowercasing.
|
||||
ASCII:
|
||||
|
||||
zend_str_tolower_copy
|
||||
zend_str_tolower_dup
|
||||
zend_str_tolower
|
||||
zend_binary_strcasecmp
|
||||
zend_binary_strncasecmp
|
||||
|
||||
Locale-based:
|
||||
zend_binary_strncasecmp_l
|
||||
zend_binary_strcasecmp_l
|
||||
zend_binary_zval_strcasecmp
|
||||
zend_binary_zval_strncasecmp
|
||||
string_compare_function_ex
|
||||
string_case_compare_function
|
||||
|
||||
Internal engine lowercasing will be using ASCII-only rules. User-facing functions,
|
||||
such as strcasecmp, will be using locale rules.
|
||||
|
||||
Two new functions - zend_binary_strncasecmp_l and zend_binary_strcasecmp_l - added as
|
||||
locale-based counterparts to zend_binary_strcasecmp and zend_binary_strncasecmp.
|
||||
|
||||
========================
|
||||
2. Build system changes
|
||||
========================
|
||||
a. New compiler flags
|
||||
- [-Wimplicit-fallthrough=1] is now enabled by default.
|
||||
It is possible to use the new ZEND_FALLTHROUGH macro keyword to suppress the warning.
|
||||
========================
|
||||
3. Module changes
|
||||
========================
|
||||
|
||||
a. ext/hash
|
||||
- The init signatures are extended with an additional `HashTable*`
|
||||
argument. The passed HT is to contain the algorithm specific
|
||||
configuration. If an algorithm doesn't make use of any additional
|
||||
configuration, the argument is to be marked with ZEND_ATTRIBUTE_UNUSED.
|
||||
a. Unix build system changes
|
||||
-
|
||||
|
||||
b. ext/pdo
|
||||
- The "preparer" callback now accepts a zend_string* instead of
|
||||
char* + size_t pair the query string. Similarly, the query_string and
|
||||
active_query_string members of pdo_stmt_t are now zend_string*.
|
||||
- The way in which drivers provide results has changed: Previously,
|
||||
the "describer" callback populated the "pdo_type" member in the
|
||||
pdo_column_data structure, and the "get_col" callback then had to return
|
||||
pointers to data of appropriate type.
|
||||
|
||||
In PHP 8.1, the "describer" callback no longer determines the pdo_type
|
||||
(and this member has been removed from pdo_column_data). Instead, the
|
||||
"get_col" callback accepts a zval pointer that may be populated with a
|
||||
value of arbitrary type. This gives drivers more flexibility in
|
||||
determining result types (e.g. based on whether a specific integer fits
|
||||
the PHP integer type) and avoid awkward juggling of temporary buffers.
|
||||
|
||||
As the "describer" no longer determines pdo_type, the "get_column_meta"
|
||||
function is now responsible for providing this information for use by
|
||||
getColumnMeta(). The type provided here does not need to match the type
|
||||
returned by get_col (in fact no corresponding type might exist, e.g. for
|
||||
floats). It should be the closest logical equivalent for the column type.
|
||||
- The transaction, set_attribute, and preparer handler's return type
|
||||
has been formalized to bool instead of int.
|
||||
- The check_liveness handler's return type has been formalized to zend_return
|
||||
instead of int.
|
||||
- The closer, and fetch_error handlers have been voidified.
|
||||
- The quoter handler now returns the quoted string as zend_string* instead
|
||||
of returning a boolean, and the quoted string as a pair of out params.
|
||||
Similarly the unquoted string is now a zend_string* instead of a pair of
|
||||
char* and size_t length.
|
||||
- The doer handler now accepts a zend_string* instead of char* + size_t
|
||||
pair for the SQL statement.
|
||||
- The last_id handler now returns a zend_string* instead of returning a
|
||||
char* and the length as an out param, and accepts a zend_string* instead
|
||||
of char* for the optional sequence/table name.
|
||||
- The php_pdo_str_tolower_dup() PDO_API has been removed use zend_str_tolower_dup()
|
||||
or zend_string_tolower_ex().
|
||||
|
||||
c. ext/standard
|
||||
- The PHP API php_fputcsv() now takes an extra zend_string* argument at the end
|
||||
for a custom EOL sequence, passing NULL provides the old default of "\n".
|
||||
|
||||
d. ext/pgsql
|
||||
- The functions php_pgsql_meta_data(), php_pgsql_convert(), php_pgsql_insert(),
|
||||
php_pgsql_update(), php_pgsql_delete(), and php_pgsql_select() have had
|
||||
their return type formalized to zend_result.
|
||||
- The functions php_pgsql_meta_data(), php_pgsql_convert(), php_pgsql_insert(),
|
||||
php_pgsql_update(), php_pgsql_delete(), and php_pgsql_select() now accept a
|
||||
zend_string* instead of a char* for the table name.
|
||||
|
||||
e. ext/pcre
|
||||
- Bundled PCRE2 is at version 10.37. The most important diff to the previous
|
||||
version is the JIT support on the S390 platform.
|
||||
b. Windows build system changes
|
||||
- Drop Windows XP and 2003 support.
|
||||
|
||||
+12
-12
@@ -1,25 +1,25 @@
|
||||
--------------------------------------------------------------------
|
||||
--------------------------------------------------------------------
|
||||
The Zend Engine License, Version 2.00
|
||||
Copyright (c) 1999-2006 Zend Technologies Ltd. All rights reserved.
|
||||
--------------------------------------------------------------------
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
|
||||
3. The names "Zend" and "Zend Engine" must not be used to endorse
|
||||
or promote products derived from this software without prior
|
||||
permission from Zend Technologies Ltd. For written permission,
|
||||
please contact license@zend.com.
|
||||
|
||||
please contact license@zend.com.
|
||||
|
||||
4. Zend Technologies Ltd. may publish revised and/or new versions
|
||||
of the license from time to time. Each version will be given a
|
||||
distinguishing version number.
|
||||
@@ -40,9 +40,9 @@ are met:
|
||||
software must display the following acknowledgment:
|
||||
"The Zend Engine is freely available at http://www.zend.com"
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY ZEND TECHNOLOGIES LTD. ``AS IS'' AND
|
||||
THIS SOFTWARE IS PROVIDED BY ZEND TECHNOLOGIES LTD. ``AS IS'' AND
|
||||
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ZEND
|
||||
TECHNOLOGIES LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
@@ -53,4 +53,4 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
--------------------------------------------------------------------
|
||||
@@ -0,0 +1,55 @@
|
||||
## Process this file with automake to produce Makefile.in -*- makefile -*-
|
||||
|
||||
#CLEANFILES = zend_language_parser.c zend_language_parser.h zend_language_scanner.c zend_language_parser.output zend_ini_parser.c zend_ini_parser.h zend_ini_scanner.c zend_ini_parser.output
|
||||
|
||||
AUTOMAKE_OPTIONS=foreign
|
||||
noinst_LTLIBRARIES=libZend.la
|
||||
|
||||
libZend_la_SOURCES=\
|
||||
zend_language_parser.y zend_language_scanner.l \
|
||||
zend_ini_parser.y zend_ini_scanner.l \
|
||||
zend_alloc.c zend_compile.c zend_constants.c zend_dynamic_array.c \
|
||||
zend_execute.c zend_execute_API.c zend_highlight.c zend_llist.c \
|
||||
zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \
|
||||
zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \
|
||||
zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \
|
||||
zend_ini.c zend_qsort.c zend_objects.c zend_object_handlers.c \
|
||||
zend_objects_API.c zend_ts_hash.c zend_stream.c \
|
||||
zend_default_classes.c \
|
||||
zend_iterators.c zend_interfaces.c zend_exceptions.c \
|
||||
zend_strtod.c zend_closures.c zend_float.c zend_string.c zend_signal.c \
|
||||
zend_generators.c
|
||||
|
||||
libZend_la_LDFLAGS =
|
||||
libZend_la_LIBADD = @ZEND_EXTRA_LIBS@
|
||||
|
||||
# automake isn't too clever about "non-standard" use of lex and yacc
|
||||
|
||||
$(libZend_la_OBJECTS): zend_language_parser.h
|
||||
|
||||
zend_ini_scanner.lo: zend_ini_parser.h
|
||||
|
||||
# Language parser/scanner rules
|
||||
|
||||
zend_language_scanner.c: $(srcdir)/zend_language_scanner.l
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date --case-inverted -cbdFt $(srcdir)/zend_language_scanner_defs.h -o$@ $(srcdir)/zend_language_scanner.l
|
||||
|
||||
zend_language_parser.h: zend_language_parser.c
|
||||
zend_language_parser.c: $(srcdir)/zend_language_parser.y
|
||||
$(YACC) -p zend -v -d $(srcdir)/zend_language_parser.y -o zend_language_parser.c
|
||||
|
||||
# INI parser/scanner rules
|
||||
|
||||
zend_ini_parser.c: $(srcdir)/zend_ini_parser.y
|
||||
$(YACC) -p ini_ -v -d $(srcdir)/zend_ini_parser.y -o zend_ini_parser.c
|
||||
|
||||
zend_ini_scanner.c: $(srcdir)/zend_ini_scanner.l
|
||||
$(RE2C) $(RE2C_FLAGS) --no-generation-date --case-inverted -cbdFt $(srcdir)/zend_ini_scanner_defs.h -o$@ $(srcdir)/zend_ini_scanner.l
|
||||
|
||||
zend_ini_parser.h: zend_ini_parser.c
|
||||
|
||||
depend:
|
||||
|
||||
zend_execute.lo: $(srcdir)/zend_execute.c
|
||||
$(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(INLINE_CFLAGS) -c $(srcdir)/zend_execute.c
|
||||
|
||||
Regular → Executable
-41
@@ -1,42 +1 @@
|
||||
#
|
||||
# Zend
|
||||
#
|
||||
|
||||
$(builddir)/zend_language_scanner.lo: $(srcdir)/zend_language_parser.h
|
||||
$(builddir)/zend_ini_scanner.lo: $(srcdir)/zend_ini_parser.h
|
||||
|
||||
$(srcdir)/zend_language_scanner.c: $(srcdir)/zend_language_scanner.l
|
||||
@(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date --case-inverted -cbdFt Zend/zend_language_scanner_defs.h -oZend/zend_language_scanner.c Zend/zend_language_scanner.l)
|
||||
|
||||
$(srcdir)/zend_language_parser.h: $(srcdir)/zend_language_parser.c
|
||||
$(srcdir)/zend_language_parser.c: $(srcdir)/zend_language_parser.y
|
||||
# Tweak zendparse to be exported through ZEND_API. This has to be revisited once
|
||||
# bison supports foreign skeletons and that bison version is used. Read
|
||||
# https://git.savannah.gnu.org/cgit/bison.git/tree/data/README.md for more.
|
||||
@$(YACC) $(YFLAGS) -v -d $(srcdir)/zend_language_parser.y -o $@
|
||||
@$(SED) -e 's,^int zendparse\(.*\),ZEND_API int zendparse\1,g' < $@ \
|
||||
> $@.tmp && \
|
||||
mv $@.tmp $@
|
||||
@$(SED) -e 's,^int zendparse\(.*\),ZEND_API int zendparse\1,g' < $(srcdir)/zend_language_parser.h \
|
||||
> $(srcdir)/zend_language_parser.h.tmp && \
|
||||
mv $(srcdir)/zend_language_parser.h.tmp $(srcdir)/zend_language_parser.h
|
||||
@nlinit=`echo 'nl="'; echo '"'`; eval "$$nlinit"; \
|
||||
$(SED) -e "s/^#ifndef YYTOKENTYPE/#include \"zend.h\"\\$${nl}#ifndef YYTOKENTYPE/" < $(srcdir)/zend_language_parser.h \
|
||||
> $(srcdir)/zend_language_parser.h.tmp && \
|
||||
mv $(srcdir)/zend_language_parser.h.tmp $(srcdir)/zend_language_parser.h
|
||||
|
||||
$(srcdir)/zend_ini_parser.h: $(srcdir)/zend_ini_parser.c
|
||||
$(srcdir)/zend_ini_parser.c: $(srcdir)/zend_ini_parser.y
|
||||
$(YACC) $(YFLAGS) -v -d $(srcdir)/zend_ini_parser.y -o $@
|
||||
|
||||
$(srcdir)/zend_ini_scanner.c: $(srcdir)/zend_ini_scanner.l
|
||||
@(cd $(top_srcdir); $(RE2C) $(RE2C_FLAGS) --no-generation-date --case-inverted -cbdFt Zend/zend_ini_scanner_defs.h -oZend/zend_ini_scanner.c Zend/zend_ini_scanner.l)
|
||||
|
||||
$(srcdir)/zend_vm_execute.h $(srcdir)/zend_vm_opcodes.c: $(srcdir)/zend_vm_def.h $(srcdir)/zend_vm_execute.skl $(srcdir)/zend_vm_gen.php
|
||||
@if test ! -z "$(PHP)"; then \
|
||||
$(PHP) $(srcdir)/zend_vm_gen.php; \
|
||||
fi;
|
||||
|
||||
$(builddir)/zend_highlight.lo $(builddir)/zend_compile.lo: $(srcdir)/zend_language_parser.h
|
||||
|
||||
Zend/zend_execute.lo: $(srcdir)/zend_vm_execute.h $(srcdir)/zend_vm_opcodes.h
|
||||
@@ -0,0 +1,195 @@
|
||||
Creating an object
|
||||
------------------
|
||||
|
||||
Object can be created in the following ways:
|
||||
|
||||
1. As a result of a function call. E.g.:
|
||||
|
||||
$foo = create_new_foo("parameter");
|
||||
$foo->run();
|
||||
|
||||
The function should create a new zval, create new object and get the
|
||||
handle for it, set handle and handler table as needed. Note that the
|
||||
handle is the only ID of the object, so it should be enough to
|
||||
identify it.
|
||||
|
||||
2. Overriding create_object handler for class. E.g.:
|
||||
|
||||
$foo = new Java("some.Class.here", "parameter");
|
||||
$foo->run();
|
||||
|
||||
The create_object handler function should create a new zval, create
|
||||
new object and get the handle for it, set handle and handler table as
|
||||
needed, and also provide constructor method that would handle
|
||||
constructor call. The get_constructor handler table entry should be
|
||||
used for that. Do not rely class entry's constructor, unless you refer
|
||||
to it from get_constructor handler.
|
||||
|
||||
Object maintenance
|
||||
------------------
|
||||
|
||||
The handlers add_ref and del_ref are called when a new zval referring
|
||||
to the object is created. This does not create a new object - both
|
||||
zvals still refer to the same object.
|
||||
|
||||
clone_obj handler should create a new object, identical to an old one,
|
||||
but being a separate entity.
|
||||
|
||||
delete_obj should destroy an object, all references to it become
|
||||
invalid.
|
||||
|
||||
Object access - read
|
||||
--------------------
|
||||
|
||||
read_property is used to read object's property. This value is not
|
||||
meant to be changed. The handler returns zval * with the value.
|
||||
|
||||
Object access - write
|
||||
---------------------
|
||||
|
||||
write_property is used to directly write object's property by
|
||||
name. This handler is used to assign property variables or to change them
|
||||
in operations like += or ++ (unless get_property_zval_ptr is also set).
|
||||
|
||||
get_property_zval_ptr is used to obtain pointer to modifiable zval for
|
||||
operations like += or ++. This should be used only if your object model
|
||||
stores properties as real zval's that can be modified from outside.
|
||||
Otherwise this handler should be NULL and the engine will use
|
||||
read_property and write_property instead.
|
||||
|
||||
get_property_ptr is used to obtain zval ** for future writing to
|
||||
it. If your object properties are stored as zval*, return real place
|
||||
where the property is stored. If the aren't, the best way is to create
|
||||
proxy object and handle it via get and set methods (see below).
|
||||
This method is meant to be used for send-by-reference and assign-by-reference
|
||||
use of object properties. If you don;t want to implement property
|
||||
referencing for your objects, you can set this handler to NULL.
|
||||
|
||||
get and set handlers are used when engine needs to access the object
|
||||
as a value. E.g., in the following situation:
|
||||
|
||||
$foo =& $obj->bar;
|
||||
$foo = 1;
|
||||
|
||||
if $foo is an object (e.g., proxy object from get_property_ptr) it
|
||||
would be accessed using write handler.
|
||||
|
||||
Object access - method call
|
||||
---------------------------
|
||||
|
||||
get_method handler is used to find method description by name. It
|
||||
should set right type, function name and parameter mask for the
|
||||
method. If the type is ZEND_OVERLOADED_FUNCTION, the method would be
|
||||
called via call_method handler, otherwise it would be called with
|
||||
standard Zend means.
|
||||
|
||||
get_constructor performs the same function as get_method, but for the
|
||||
object constructor.
|
||||
|
||||
call_method handler is used to perform method call. Parameters are
|
||||
passed like to any other Zend internal function.
|
||||
|
||||
Object - comparison
|
||||
-------------------
|
||||
|
||||
Objects can be compared via compare_objects handler. This is used with
|
||||
== operation, === compares objects by handles, i.e., return true if
|
||||
and only if it's really the same object. Note that objects from
|
||||
different object types (i.e., having different handlers) can not be
|
||||
compared.
|
||||
|
||||
Objects - reflection
|
||||
--------------------
|
||||
|
||||
get_class_name is used to retrieve class name of the object.
|
||||
get_class_entry returns class entry (zend_class_entry) for the object,
|
||||
in case there exists PHP class for it.
|
||||
No other reflection functions are currently implemented.
|
||||
|
||||
Objects - data structures and handlers
|
||||
---------------------------------------
|
||||
|
||||
The object is represented by the following structure:
|
||||
|
||||
struct _zend_object_value {
|
||||
zend_object_handle handle;
|
||||
zend_object_handlers *handlers;
|
||||
};
|
||||
|
||||
handle is an ID of the object among the objects of the same type (not
|
||||
class!). The type of the object and how it behaves is determined by
|
||||
the handler table.
|
||||
|
||||
typedef struct _zend_object_handlers {
|
||||
zend_object_add_ref_t add_ref;
|
||||
zend_object_del_ref_t del_ref;
|
||||
zend_object_delete_obj_t delete_obj;
|
||||
zend_object_clone_obj_t clone_obj;
|
||||
zend_object_read_property_t read_property;
|
||||
zend_object_write_property_t write_property;
|
||||
zend_object_get_property_ptr_t get_property_ptr;
|
||||
zend_object_get_property_zval_ptr_t get_property_zval_ptr;
|
||||
zend_object_get_t get;
|
||||
zend_object_set_t set;
|
||||
zend_object_has_property_t has_property;
|
||||
zend_object_unset_property_t unset_property;
|
||||
zend_object_get_properties_t get_properties;
|
||||
zend_object_get_method_t get_method;
|
||||
zend_object_call_method_t call_method;
|
||||
zend_object_get_constructor_t get_constructor;
|
||||
zend_object_get_class_entry_t get_class_entry;
|
||||
zend_object_get_class_name_t get_class_name;
|
||||
zend_object_compare_t compare_objects;
|
||||
} zend_object_handlers;
|
||||
|
||||
See zend_object_handlers.h for prototypes. All objects are passed as zval's.
|
||||
|
||||
Handlers explained:
|
||||
|
||||
add_ref - called when a copy of the object handle is created.
|
||||
|
||||
del_ref - called when a copy of the object handle is destroyed.
|
||||
|
||||
delete_obj - called when an object needs to be destroyed.
|
||||
|
||||
clone_obj - called when a new object identical to an old one should be
|
||||
created (unlike Zend Engine 1, this never happens unless explicitly
|
||||
asked for).
|
||||
|
||||
read_property - returns zval *, containing the value of the
|
||||
property. Is used when value of the property should be retrieved for
|
||||
reading.
|
||||
|
||||
write_property - assigns value to certain property of the object.
|
||||
|
||||
get_property_zval_ptr - retrieves zval** for being directly modified by
|
||||
the engine. If your properties are not zval's, don't define it.
|
||||
|
||||
get_property_ptr - retrieves zval** for the property of the value, to
|
||||
be used for read and write. If object properties are not zval's
|
||||
natively, this method should create and return proxy object for use
|
||||
with get and set methods.
|
||||
|
||||
get - retrieves zval* for object contents. To be used mainly with
|
||||
proxy objects from get_property_ptr, but also may be used for
|
||||
convert_to_* functions.
|
||||
|
||||
set - sets value for object contents. To be used mainly with
|
||||
proxy objects from get_property_ptr.
|
||||
|
||||
has_property - checks if the object has certain property set.
|
||||
|
||||
unset_property - removes value for the property of the object
|
||||
|
||||
get_method - retrieves description of the method
|
||||
|
||||
call_method - calls the method (parameters should be put on stack like
|
||||
for any other PHP internal function).
|
||||
|
||||
get_constructor - get description for the object constructor method
|
||||
|
||||
get_class_entry - should return the class entry for the object
|
||||
|
||||
get_class_name - get the name of the class the object belongs to
|
||||
|
||||
compare_objects - compares if two objects are equal
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,864 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
| Xinchen Hui <laruence@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 11
|
||||
* - compact literals table
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
#include "zend_extensions.h"
|
||||
|
||||
#define DEBUG_COMPACT_LITERALS 0
|
||||
|
||||
#define LITERAL_VALUE 0x0100
|
||||
#define LITERAL_FUNC 0x0200
|
||||
#define LITERAL_CLASS 0x0300
|
||||
#define LITERAL_CONST 0x0400
|
||||
#define LITERAL_CLASS_CONST 0x0500
|
||||
#define LITERAL_STATIC_METHOD 0x0600
|
||||
#define LITERAL_STATIC_PROPERTY 0x0700
|
||||
#define LITERAL_METHOD 0x0800
|
||||
#define LITERAL_PROPERTY 0x0900
|
||||
#define LITERAL_GLOBAL 0x0A00
|
||||
|
||||
#define LITERAL_KIND_MASK 0x0f00
|
||||
#define LITERAL_NUM_RELATED_MASK 0x000f
|
||||
|
||||
#define LITERAL_NUM_RELATED(info) (info & LITERAL_NUM_RELATED_MASK)
|
||||
|
||||
typedef struct _literal_info {
|
||||
uint32_t flags; /* bitmask (see defines above) */
|
||||
} literal_info;
|
||||
|
||||
#define LITERAL_INFO(n, kind, related) do { \
|
||||
info[n].flags = ((kind) | (related)); \
|
||||
} while (0)
|
||||
|
||||
static size_t type_num_classes(const zend_op_array *op_array, uint32_t arg_num)
|
||||
{
|
||||
zend_arg_info *arg_info;
|
||||
if (arg_num > 0) {
|
||||
if (!(op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS)) {
|
||||
return 0;
|
||||
}
|
||||
if (EXPECTED(arg_num <= op_array->num_args)) {
|
||||
arg_info = &op_array->arg_info[arg_num-1];
|
||||
} else if (UNEXPECTED(op_array->fn_flags & ZEND_ACC_VARIADIC)) {
|
||||
arg_info = &op_array->arg_info[op_array->num_args];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
arg_info = op_array->arg_info - 1;
|
||||
}
|
||||
|
||||
if (ZEND_TYPE_HAS_CLASS(arg_info->type)) {
|
||||
if (ZEND_TYPE_HAS_LIST(arg_info->type)) {
|
||||
return ZEND_TYPE_LIST(arg_info->type)->num_types;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t add_static_slot(HashTable *hash,
|
||||
zend_op_array *op_array,
|
||||
uint32_t op1,
|
||||
uint32_t op2,
|
||||
uint32_t kind,
|
||||
int *cache_size)
|
||||
{
|
||||
uint32_t ret;
|
||||
zval *class_name = &op_array->literals[op1];
|
||||
zval *prop_name = &op_array->literals[op2];
|
||||
zval *pos, tmp;
|
||||
|
||||
zend_string *key = zend_create_member_string(Z_STR_P(class_name), Z_STR_P(prop_name));
|
||||
ZSTR_H(key) = zend_string_hash_func(key);
|
||||
ZSTR_H(key) += kind;
|
||||
|
||||
pos = zend_hash_find(hash, key);
|
||||
if (pos) {
|
||||
ret = Z_LVAL_P(pos);
|
||||
} else {
|
||||
ret = *cache_size;
|
||||
*cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *);
|
||||
ZVAL_LONG(&tmp, ret);
|
||||
zend_hash_add(hash, key, &tmp);
|
||||
}
|
||||
zend_string_release_ex(key, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static zend_string *create_str_cache_key(zval *literal, uint32_t flags)
|
||||
{
|
||||
ZEND_ASSERT(Z_TYPE_P(literal) == IS_STRING);
|
||||
uint32_t num_related = LITERAL_NUM_RELATED(flags);
|
||||
if (num_related == 1) {
|
||||
return zend_string_copy(Z_STR_P(literal));
|
||||
}
|
||||
if ((flags & LITERAL_KIND_MASK) == LITERAL_VALUE) {
|
||||
/* Don't merge LITERAL_VALUE that has related literals */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Concatenate all the related literals for the cache key. */
|
||||
zend_string *key;
|
||||
if (num_related == 2) {
|
||||
ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING);
|
||||
key = zend_string_concat2(
|
||||
Z_STRVAL_P(literal), Z_STRLEN_P(literal),
|
||||
Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1));
|
||||
} else if (num_related == 3) {
|
||||
ZEND_ASSERT(Z_TYPE_P(literal + 1) == IS_STRING && Z_TYPE_P(literal + 2) == IS_STRING);
|
||||
key = zend_string_concat3(
|
||||
Z_STRVAL_P(literal), Z_STRLEN_P(literal),
|
||||
Z_STRVAL_P(literal + 1), Z_STRLEN_P(literal + 1),
|
||||
Z_STRVAL_P(literal + 2), Z_STRLEN_P(literal + 2));
|
||||
} else {
|
||||
ZEND_ASSERT(0 && "Currently not needed");
|
||||
}
|
||||
|
||||
/* Add a bias to the hash so we can distinguish keys
|
||||
* that would otherwise be the same after concatenation. */
|
||||
ZSTR_H(key) = zend_string_hash_val(key) + num_related - 1;
|
||||
return key;
|
||||
}
|
||||
|
||||
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *opline, *end;
|
||||
int i, j, n, *map, cache_size;
|
||||
zval zv, *pos;
|
||||
literal_info *info;
|
||||
int l_null = -1;
|
||||
int l_false = -1;
|
||||
int l_true = -1;
|
||||
int l_empty_arr = -1;
|
||||
HashTable hash, double_hash;
|
||||
zend_string *key = NULL;
|
||||
void *checkpoint = zend_arena_checkpoint(ctx->arena);
|
||||
int *const_slot, *class_slot, *func_slot, *bind_var_slot, *property_slot, *method_slot;
|
||||
|
||||
if (op_array->last_literal) {
|
||||
info = (literal_info*)zend_arena_calloc(&ctx->arena, op_array->last_literal, sizeof(literal_info));
|
||||
|
||||
/* Mark literals of specific types */
|
||||
opline = op_array->opcodes;
|
||||
end = opline + op_array->last;
|
||||
while (opline < end) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_FCALL:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 1);
|
||||
break;
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 2);
|
||||
break;
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_FUNC, 3);
|
||||
break;
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_METHOD, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_STATIC_METHOD, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_CATCH:
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
|
||||
break;
|
||||
case ZEND_DEFINED:
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CONST, 1);
|
||||
break;
|
||||
case ZEND_FETCH_CONSTANT:
|
||||
if (opline->op1.num & IS_CONSTANT_UNQUALIFIED_IN_NAMESPACE) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 3);
|
||||
} else {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CONST, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CLASS_CONSTANT:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1);
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
case ZEND_FETCH_STATIC_PROP_R:
|
||||
case ZEND_FETCH_STATIC_PROP_W:
|
||||
case ZEND_FETCH_STATIC_PROP_RW:
|
||||
case ZEND_FETCH_STATIC_PROP_IS:
|
||||
case ZEND_FETCH_STATIC_PROP_UNSET:
|
||||
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
|
||||
case ZEND_UNSET_STATIC_PROP:
|
||||
case ZEND_ISSET_ISEMPTY_STATIC_PROP:
|
||||
case ZEND_PRE_INC_STATIC_PROP:
|
||||
case ZEND_PRE_DEC_STATIC_PROP:
|
||||
case ZEND_POST_INC_STATIC_PROP:
|
||||
case ZEND_POST_DEC_STATIC_PROP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_STATIC_PROPERTY, 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CLASS:
|
||||
case ZEND_INSTANCEOF:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_NEW:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_CLASS, 2);
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_FETCH_OBJ_R:
|
||||
case ZEND_FETCH_OBJ_W:
|
||||
case ZEND_FETCH_OBJ_RW:
|
||||
case ZEND_FETCH_OBJ_IS:
|
||||
case ZEND_FETCH_OBJ_UNSET:
|
||||
case ZEND_FETCH_OBJ_FUNC_ARG:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_BIND_GLOBAL:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_GLOBAL, 1);
|
||||
break;
|
||||
case ZEND_RECV_INIT:
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
|
||||
break;
|
||||
case ZEND_DECLARE_CLASS:
|
||||
case ZEND_DECLARE_CLASS_DELAYED:
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 2);
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_UNSET_DIM:
|
||||
case ZEND_FETCH_DIM_R:
|
||||
case ZEND_FETCH_DIM_W:
|
||||
case ZEND_FETCH_DIM_RW:
|
||||
case ZEND_FETCH_DIM_IS:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_UNSET:
|
||||
case ZEND_FETCH_LIST_R:
|
||||
case ZEND_FETCH_LIST_W:
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
if (Z_EXTRA(op_array->literals[opline->op2.constant]) == ZEND_EXTRA_VALUE) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 2);
|
||||
} else {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op1.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
LITERAL_INFO(opline->op2.constant, LITERAL_VALUE, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
|
||||
#if DEBUG_COMPACT_LITERALS
|
||||
{
|
||||
int i, use_copy;
|
||||
fprintf(stderr, "File %s func %s\n", op_array->filename->val,
|
||||
op_array->function_name ? op_array->function_name->val : "main");
|
||||
fprintf(stderr, "Literals table size %d\n", op_array->last_literal);
|
||||
|
||||
for (i = 0; i < op_array->last_literal; i++) {
|
||||
zval zv;
|
||||
ZVAL_COPY_VALUE(&zv, op_array->literals + i);
|
||||
use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
|
||||
fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
|
||||
if (use_copy) {
|
||||
zval_ptr_dtor_nogc(&zv);
|
||||
}
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Merge equal constants */
|
||||
j = 0;
|
||||
zend_hash_init(&hash, op_array->last_literal, NULL, NULL, 0);
|
||||
/* Use separate hashtable for doubles stored as string keys, to avoid collisions. */
|
||||
zend_hash_init(&double_hash, 0, NULL, NULL, 0);
|
||||
map = (int*)zend_arena_alloc(&ctx->arena, op_array->last_literal * sizeof(int));
|
||||
memset(map, 0, op_array->last_literal * sizeof(int));
|
||||
for (i = 0; i < op_array->last_literal; i++) {
|
||||
if (!info[i].flags) {
|
||||
/* unset literal */
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i]);
|
||||
continue;
|
||||
}
|
||||
switch (Z_TYPE(op_array->literals[i])) {
|
||||
case IS_NULL:
|
||||
if (l_null < 0) {
|
||||
l_null = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
map[i] = l_null;
|
||||
break;
|
||||
case IS_FALSE:
|
||||
if (l_false < 0) {
|
||||
l_false = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
map[i] = l_false;
|
||||
break;
|
||||
case IS_TRUE:
|
||||
if (l_true < 0) {
|
||||
l_true = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
map[i] = l_true;
|
||||
break;
|
||||
case IS_LONG:
|
||||
if (LITERAL_NUM_RELATED(info[i].flags) == 1) {
|
||||
if ((pos = zend_hash_index_find(&hash, Z_LVAL(op_array->literals[i]))) != NULL) {
|
||||
map[i] = Z_LVAL_P(pos);
|
||||
} else {
|
||||
map[i] = j;
|
||||
ZVAL_LONG(&zv, j);
|
||||
zend_hash_index_add_new(&hash, Z_LVAL(op_array->literals[i]), &zv);
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(LITERAL_NUM_RELATED(info[i].flags) == 2);
|
||||
key = zend_string_init(Z_STRVAL(op_array->literals[i+1]), Z_STRLEN(op_array->literals[i+1]), 0);
|
||||
ZSTR_H(key) = ZSTR_HASH(Z_STR(op_array->literals[i+1])) + 100 +
|
||||
LITERAL_NUM_RELATED(info[i].flags) - 1;
|
||||
if ((pos = zend_hash_find(&hash, key)) != NULL
|
||||
&& LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) == 2) {
|
||||
map[i] = Z_LVAL_P(pos);
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i+1]);
|
||||
} else {
|
||||
map[i] = j;
|
||||
ZVAL_LONG(&zv, j);
|
||||
zend_hash_add_new(&hash, key, &zv);
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
op_array->literals[j+1] = op_array->literals[i+1];
|
||||
info[j+1] = info[i+1];
|
||||
}
|
||||
j += 2;
|
||||
}
|
||||
zend_string_release_ex(key, 0);
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case IS_DOUBLE:
|
||||
if ((pos = zend_hash_str_find(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double))) != NULL) {
|
||||
map[i] = Z_LVAL_P(pos);
|
||||
} else {
|
||||
map[i] = j;
|
||||
ZVAL_LONG(&zv, j);
|
||||
zend_hash_str_add_new(&double_hash, (char*)&Z_DVAL(op_array->literals[i]), sizeof(double), &zv);
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
break;
|
||||
case IS_STRING: {
|
||||
key = create_str_cache_key(&op_array->literals[i], info[i].flags);
|
||||
if (key && (pos = zend_hash_find(&hash, key)) != NULL &&
|
||||
Z_TYPE(op_array->literals[Z_LVAL_P(pos)]) == IS_STRING &&
|
||||
LITERAL_NUM_RELATED(info[i].flags) == LITERAL_NUM_RELATED(info[Z_LVAL_P(pos)].flags) &&
|
||||
(LITERAL_NUM_RELATED(info[i].flags) != 2 ||
|
||||
((info[i].flags & LITERAL_KIND_MASK) != LITERAL_VALUE &&
|
||||
(info[Z_LVAL_P(pos)].flags & LITERAL_KIND_MASK) != LITERAL_VALUE))) {
|
||||
zend_string_release_ex(key, 0);
|
||||
map[i] = Z_LVAL_P(pos);
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i]);
|
||||
n = LITERAL_NUM_RELATED(info[i].flags);
|
||||
while (n > 1) {
|
||||
i++;
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i]);
|
||||
n--;
|
||||
}
|
||||
} else {
|
||||
map[i] = j;
|
||||
ZVAL_LONG(&zv, j);
|
||||
if (key) {
|
||||
zend_hash_add_new(&hash, key, &zv);
|
||||
zend_string_release_ex(key, 0);
|
||||
}
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
n = LITERAL_NUM_RELATED(info[i].flags);
|
||||
while (n > 1) {
|
||||
i++;
|
||||
if (i != j) op_array->literals[j] = op_array->literals[i];
|
||||
j++;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IS_ARRAY:
|
||||
if (zend_hash_num_elements(Z_ARRVAL(op_array->literals[i])) == 0) {
|
||||
if (l_empty_arr < 0) {
|
||||
l_empty_arr = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
} else {
|
||||
zval_ptr_dtor_nogc(&op_array->literals[i]);
|
||||
}
|
||||
map[i] = l_empty_arr;
|
||||
break;
|
||||
}
|
||||
ZEND_FALLTHROUGH;
|
||||
default:
|
||||
/* don't merge other types */
|
||||
map[i] = j;
|
||||
if (i != j) {
|
||||
op_array->literals[j] = op_array->literals[i];
|
||||
info[j] = info[i];
|
||||
}
|
||||
j++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only clean "hash", as it will be reused in the loop below. */
|
||||
zend_hash_clean(&hash);
|
||||
zend_hash_destroy(&double_hash);
|
||||
op_array->last_literal = j;
|
||||
|
||||
const_slot = zend_arena_alloc(&ctx->arena, j * 6 * sizeof(int));
|
||||
memset(const_slot, -1, j * 6 * sizeof(int));
|
||||
class_slot = const_slot + j;
|
||||
func_slot = class_slot + j;
|
||||
bind_var_slot = func_slot + j;
|
||||
property_slot = bind_var_slot + j;
|
||||
method_slot = property_slot + j;
|
||||
|
||||
/* Update opcodes to use new literals table */
|
||||
cache_size = zend_op_array_extension_handles * sizeof(void*);
|
||||
opline = op_array->opcodes;
|
||||
end = opline + op_array->last;
|
||||
while (opline < end) {
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
opline->op1.constant = map[opline->op1.constant];
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
opline->op2.constant = map[opline->op2.constant];
|
||||
}
|
||||
switch (opline->opcode) {
|
||||
case ZEND_RECV_INIT:
|
||||
case ZEND_RECV:
|
||||
case ZEND_RECV_VARIADIC:
|
||||
{
|
||||
size_t num_classes = type_num_classes(op_array, opline->op1.num);
|
||||
if (num_classes) {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += num_classes * sizeof(void *);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZEND_VERIFY_RETURN_TYPE:
|
||||
{
|
||||
size_t num_classes = type_num_classes(op_array, 0);
|
||||
if (num_classes) {
|
||||
opline->op2.num = cache_size;
|
||||
cache_size += num_classes * sizeof(void *);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1 static property
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
(opline+1)->extended_value = add_static_slot(&hash, op_array,
|
||||
opline->op2.constant,
|
||||
opline->op1.constant,
|
||||
LITERAL_STATIC_PROPERTY,
|
||||
&cache_size);
|
||||
} else {
|
||||
(opline+1)->extended_value = cache_size;
|
||||
cache_size += 3 * sizeof(void *);
|
||||
}
|
||||
} else if (opline->op2_type == IS_CONST) {
|
||||
// op2 class
|
||||
if (class_slot[opline->op2.constant] >= 0) {
|
||||
(opline+1)->extended_value = class_slot[opline->op2.constant];
|
||||
} else {
|
||||
(opline+1)->extended_value = cache_size;
|
||||
class_slot[opline->op2.constant] = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 property
|
||||
if (opline->op1_type == IS_UNUSED &&
|
||||
property_slot[opline->op2.constant] >= 0) {
|
||||
(opline+1)->extended_value = property_slot[opline->op2.constant];
|
||||
} else {
|
||||
(opline+1)->extended_value = cache_size;
|
||||
cache_size += 3 * sizeof(void *);
|
||||
if (opline->op1_type == IS_UNUSED) {
|
||||
property_slot[opline->op2.constant] = (opline+1)->extended_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_FETCH_OBJ_R:
|
||||
case ZEND_FETCH_OBJ_W:
|
||||
case ZEND_FETCH_OBJ_RW:
|
||||
case ZEND_FETCH_OBJ_IS:
|
||||
case ZEND_FETCH_OBJ_UNSET:
|
||||
case ZEND_FETCH_OBJ_FUNC_ARG:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 property
|
||||
if (opline->op1_type == IS_UNUSED &&
|
||||
property_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
cache_size += 3 * sizeof(void *);
|
||||
if (opline->op1_type == IS_UNUSED) {
|
||||
property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 property
|
||||
if (opline->op1_type == IS_UNUSED &&
|
||||
property_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY);
|
||||
cache_size += 3 * sizeof(void *);
|
||||
if (opline->op1_type == IS_UNUSED) {
|
||||
property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
// op2 func
|
||||
if (func_slot[opline->op2.constant] >= 0) {
|
||||
opline->result.num = func_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
func_slot[opline->op2.constant] = opline->result.num;
|
||||
}
|
||||
break;
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 method
|
||||
if (opline->op1_type == IS_UNUSED &&
|
||||
method_slot[opline->op2.constant] >= 0) {
|
||||
opline->result.num = method_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += 2 * sizeof(void *);
|
||||
if (opline->op1_type == IS_UNUSED) {
|
||||
method_slot[opline->op2.constant] = opline->result.num;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 static method
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
opline->result.num = add_static_slot(&hash, op_array,
|
||||
opline->op1.constant,
|
||||
opline->op2.constant,
|
||||
LITERAL_STATIC_METHOD,
|
||||
&cache_size);
|
||||
} else {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += 2 * sizeof(void *);
|
||||
}
|
||||
} else if (opline->op1_type == IS_CONST) {
|
||||
// op1 class
|
||||
if (class_slot[opline->op1.constant] >= 0) {
|
||||
opline->result.num = class_slot[opline->op1.constant];
|
||||
} else {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
class_slot[opline->op1.constant] = opline->result.num;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_DEFINED:
|
||||
// op1 const
|
||||
if (const_slot[opline->op1.constant] >= 0) {
|
||||
opline->extended_value = const_slot[opline->op1.constant];
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
const_slot[opline->op1.constant] = opline->extended_value;
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CONSTANT:
|
||||
// op2 const
|
||||
if (const_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = const_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
const_slot[opline->op2.constant] = opline->extended_value;
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CLASS_CONSTANT:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1/op2 class_const
|
||||
opline->extended_value = add_static_slot(&hash, op_array,
|
||||
opline->op1.constant,
|
||||
opline->op2.constant,
|
||||
LITERAL_CLASS_CONST,
|
||||
&cache_size);
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += 2 * sizeof(void *);
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
case ZEND_FETCH_STATIC_PROP_R:
|
||||
case ZEND_FETCH_STATIC_PROP_W:
|
||||
case ZEND_FETCH_STATIC_PROP_RW:
|
||||
case ZEND_FETCH_STATIC_PROP_IS:
|
||||
case ZEND_FETCH_STATIC_PROP_UNSET:
|
||||
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
|
||||
case ZEND_UNSET_STATIC_PROP:
|
||||
case ZEND_ISSET_ISEMPTY_STATIC_PROP:
|
||||
case ZEND_PRE_INC_STATIC_PROP:
|
||||
case ZEND_PRE_DEC_STATIC_PROP:
|
||||
case ZEND_POST_INC_STATIC_PROP:
|
||||
case ZEND_POST_DEC_STATIC_PROP:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1 static property
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
opline->extended_value = add_static_slot(&hash, op_array,
|
||||
opline->op2.constant,
|
||||
opline->op1.constant,
|
||||
LITERAL_STATIC_PROPERTY,
|
||||
&cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
cache_size += 3 * sizeof(void *);
|
||||
}
|
||||
} else if (opline->op2_type == IS_CONST) {
|
||||
// op2 class
|
||||
if (class_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS);
|
||||
class_slot[opline->op2.constant] = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_FETCH_CLASS:
|
||||
case ZEND_INSTANCEOF:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
// op2 class
|
||||
if (class_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = class_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
class_slot[opline->op2.constant] = opline->extended_value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_NEW:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1 class
|
||||
if (class_slot[opline->op1.constant] >= 0) {
|
||||
opline->op2.num = class_slot[opline->op1.constant];
|
||||
} else {
|
||||
opline->op2.num = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
class_slot[opline->op1.constant] = opline->op2.num;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_CATCH:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
// op1 class
|
||||
if (class_slot[opline->op1.constant] >= 0) {
|
||||
opline->extended_value = class_slot[opline->op1.constant] | (opline->extended_value & ZEND_LAST_CATCH);
|
||||
} else {
|
||||
opline->extended_value = cache_size | (opline->extended_value & ZEND_LAST_CATCH);
|
||||
cache_size += sizeof(void *);
|
||||
class_slot[opline->op1.constant] = opline->extended_value & ~ZEND_LAST_CATCH;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_BIND_GLOBAL:
|
||||
// op2 bind var
|
||||
if (bind_var_slot[opline->op2.constant] >= 0) {
|
||||
opline->extended_value = bind_var_slot[opline->op2.constant];
|
||||
} else {
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
bind_var_slot[opline->op2.constant] = opline->extended_value;
|
||||
}
|
||||
break;
|
||||
case ZEND_DECLARE_ANON_CLASS:
|
||||
case ZEND_DECLARE_CLASS_DELAYED:
|
||||
opline->extended_value = cache_size;
|
||||
cache_size += sizeof(void *);
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
opline->result.num = cache_size;
|
||||
cache_size += 2 * sizeof(void *);
|
||||
}
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
op_array->cache_size = cache_size;
|
||||
zend_hash_destroy(&hash);
|
||||
zend_arena_release(&ctx->arena, checkpoint);
|
||||
|
||||
if (1) {
|
||||
opline = op_array->opcodes;
|
||||
while (1) {
|
||||
if (opline->opcode == ZEND_RECV_INIT) {
|
||||
zval *val = &op_array->literals[opline->op2.constant];
|
||||
|
||||
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
|
||||
/* Ensure zval is aligned to 8 bytes */
|
||||
op_array->cache_size = ZEND_MM_ALIGNED_SIZE_EX(op_array->cache_size, 8);
|
||||
Z_CACHE_SLOT_P(val) = op_array->cache_size;
|
||||
op_array->cache_size += sizeof(zval);
|
||||
}
|
||||
} else if (opline->opcode != ZEND_RECV) {
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG_COMPACT_LITERALS
|
||||
{
|
||||
int i, use_copy;
|
||||
fprintf(stderr, "Optimized literals table size %d\n", op_array->last_literal);
|
||||
|
||||
for (i = 0; i < op_array->last_literal; i++) {
|
||||
zval zv;
|
||||
ZVAL_COPY_VALUE(&zv, op_array->literals + i);
|
||||
use_copy = zend_make_printable_zval(op_array->literals + i, &zv);
|
||||
fprintf(stderr, "Literal %d, val (%zu):%s\n", i, Z_STRLEN(zv), Z_STRVAL(zv));
|
||||
if (use_copy) {
|
||||
zval_ptr_dtor_nogc(&zv);
|
||||
}
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Removing unused variables |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Nikita Popov <nikic@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
/* This pass removes all CVs and temporaries that are completely unused. It does *not* merge any CVs or TMPs.
|
||||
* This pass does not operate on SSA form anymore. */
|
||||
void zend_optimizer_compact_vars(zend_op_array *op_array) {
|
||||
int i;
|
||||
|
||||
ALLOCA_FLAG(use_heap1);
|
||||
ALLOCA_FLAG(use_heap2);
|
||||
uint32_t used_vars_len = zend_bitset_len(op_array->last_var + op_array->T);
|
||||
zend_bitset used_vars = ZEND_BITSET_ALLOCA(used_vars_len, use_heap1);
|
||||
uint32_t *vars_map = do_alloca((op_array->last_var + op_array->T) * sizeof(uint32_t), use_heap2);
|
||||
uint32_t num_cvs, num_tmps;
|
||||
|
||||
/* Determine which CVs are used */
|
||||
zend_bitset_clear(used_vars, used_vars_len);
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
zend_op *opline = &op_array->opcodes[i];
|
||||
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
zend_bitset_incl(used_vars, VAR_NUM(opline->op1.var));
|
||||
}
|
||||
if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
zend_bitset_incl(used_vars, VAR_NUM(opline->op2.var));
|
||||
}
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
zend_bitset_incl(used_vars, VAR_NUM(opline->result.var));
|
||||
if (opline->opcode == ZEND_ROPE_INIT) {
|
||||
uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
|
||||
while (num > 1) {
|
||||
num--;
|
||||
zend_bitset_incl(used_vars, VAR_NUM(opline->result.var) + num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
num_cvs = 0;
|
||||
for (i = 0; i < op_array->last_var; i++) {
|
||||
if (zend_bitset_in(used_vars, i)) {
|
||||
vars_map[i] = num_cvs++;
|
||||
} else {
|
||||
vars_map[i] = (uint32_t) -1;
|
||||
}
|
||||
}
|
||||
|
||||
num_tmps = 0;
|
||||
for (i = op_array->last_var; i < op_array->last_var + op_array->T; i++) {
|
||||
if (zend_bitset_in(used_vars, i)) {
|
||||
vars_map[i] = num_cvs + num_tmps++;
|
||||
} else {
|
||||
vars_map[i] = (uint32_t) -1;
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(used_vars, use_heap1);
|
||||
if (num_cvs == op_array->last_var && num_tmps == op_array->T) {
|
||||
free_alloca(vars_map, use_heap2);
|
||||
return;
|
||||
}
|
||||
|
||||
ZEND_ASSERT(num_cvs <= op_array->last_var);
|
||||
ZEND_ASSERT(num_tmps <= op_array->T);
|
||||
|
||||
/* Update CV and TMP references in opcodes */
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
zend_op *opline = &op_array->opcodes[i];
|
||||
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
opline->op1.var = NUM_VAR(vars_map[VAR_NUM(opline->op1.var)]);
|
||||
}
|
||||
if (opline->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
opline->op2.var = NUM_VAR(vars_map[VAR_NUM(opline->op2.var)]);
|
||||
}
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
opline->result.var = NUM_VAR(vars_map[VAR_NUM(opline->result.var)]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Update CV name table */
|
||||
if (num_cvs != op_array->last_var) {
|
||||
if (num_cvs) {
|
||||
zend_string **names = safe_emalloc(sizeof(zend_string *), num_cvs, 0);
|
||||
for (i = 0; i < op_array->last_var; i++) {
|
||||
if (vars_map[i] != (uint32_t) -1) {
|
||||
names[vars_map[i]] = op_array->vars[i];
|
||||
} else {
|
||||
zend_string_release_ex(op_array->vars[i], 0);
|
||||
}
|
||||
}
|
||||
efree(op_array->vars);
|
||||
op_array->vars = names;
|
||||
} else {
|
||||
for (i = 0; i < op_array->last_var; i++) {
|
||||
zend_string_release_ex(op_array->vars[i], 0);
|
||||
}
|
||||
efree(op_array->vars);
|
||||
op_array->vars = NULL;
|
||||
}
|
||||
op_array->last_var = num_cvs;
|
||||
}
|
||||
|
||||
op_array->T = num_tmps;
|
||||
|
||||
free_alloca(vars_map, use_heap2);
|
||||
}
|
||||
@@ -1,651 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, DCE - Dead Code Elimination |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Nikita Popov <nikic@php.net> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "Optimizer/zend_inference.h"
|
||||
#include "Optimizer/zend_ssa.h"
|
||||
#include "Optimizer/zend_func_info.h"
|
||||
#include "Optimizer/zend_call_graph.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
/* This pass implements a form of dead code elimination (DCE). The algorithm optimistically assumes
|
||||
* that all instructions and phis are dead. Instructions with immediate side-effects are then marked
|
||||
* as live. We then recursively (using a worklist) propagate liveness to the instructions that def
|
||||
* the used operands.
|
||||
*
|
||||
* Notes:
|
||||
* * This pass does not perform unreachable code elimination. This happens as part of the SCCP
|
||||
* pass.
|
||||
* * The DCE is performed without taking control-dependence into account, i.e. all conditional
|
||||
* branches are assumed to be live. It's possible to take control-dependence into account using
|
||||
* the DCE algorithm described by Cytron et al., however it requires the construction of a
|
||||
* postdominator tree and of postdominance frontiers, which does not seem worthwhile at this
|
||||
* point.
|
||||
* * We separate intrinsic side-effects from potential side-effects in the form of notices thrown
|
||||
* by the instruction (in case we want to make this configurable). See may_have_side_effects() and
|
||||
* zend_may_throw().
|
||||
* * We often cannot DCE assignments and unsets while guaranteeing that dtors run in the same
|
||||
* order. There is an optimization option to allow reordering of dtor effects.
|
||||
* * The algorithm is able to eliminate dead modifications of non-escaping arrays
|
||||
* and objects as well as dead arrays and objects allocations.
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
zend_ssa *ssa;
|
||||
zend_op_array *op_array;
|
||||
zend_bitset instr_dead;
|
||||
zend_bitset phi_dead;
|
||||
zend_bitset instr_worklist;
|
||||
zend_bitset phi_worklist;
|
||||
zend_bitset phi_worklist_no_val;
|
||||
uint32_t instr_worklist_len;
|
||||
uint32_t phi_worklist_len;
|
||||
unsigned reorder_dtor_effects : 1;
|
||||
} context;
|
||||
|
||||
static inline bool is_bad_mod(const zend_ssa *ssa, int use, int def) {
|
||||
if (def < 0) {
|
||||
/* This modification is not tracked by SSA, assume the worst */
|
||||
return 1;
|
||||
}
|
||||
if (ssa->var_info[use].type & MAY_BE_REF) {
|
||||
/* Modification of reference may have side-effect */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool may_have_side_effects(
|
||||
zend_op_array *op_array, zend_ssa *ssa,
|
||||
const zend_op *opline, const zend_ssa_op *ssa_op,
|
||||
bool reorder_dtor_effects) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_NOP:
|
||||
case ZEND_IS_IDENTICAL:
|
||||
case ZEND_IS_NOT_IDENTICAL:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_FREE:
|
||||
case ZEND_FE_FREE:
|
||||
case ZEND_TYPE_CHECK:
|
||||
case ZEND_DEFINED:
|
||||
case ZEND_ADD:
|
||||
case ZEND_SUB:
|
||||
case ZEND_MUL:
|
||||
case ZEND_POW:
|
||||
case ZEND_BW_OR:
|
||||
case ZEND_BW_AND:
|
||||
case ZEND_BW_XOR:
|
||||
case ZEND_CONCAT:
|
||||
case ZEND_FAST_CONCAT:
|
||||
case ZEND_DIV:
|
||||
case ZEND_MOD:
|
||||
case ZEND_BOOL_XOR:
|
||||
case ZEND_BOOL:
|
||||
case ZEND_BOOL_NOT:
|
||||
case ZEND_BW_NOT:
|
||||
case ZEND_SL:
|
||||
case ZEND_SR:
|
||||
case ZEND_IS_EQUAL:
|
||||
case ZEND_IS_NOT_EQUAL:
|
||||
case ZEND_IS_SMALLER:
|
||||
case ZEND_IS_SMALLER_OR_EQUAL:
|
||||
case ZEND_CASE:
|
||||
case ZEND_CASE_STRICT:
|
||||
case ZEND_CAST:
|
||||
case ZEND_ROPE_INIT:
|
||||
case ZEND_ROPE_ADD:
|
||||
case ZEND_INIT_ARRAY:
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
case ZEND_SPACESHIP:
|
||||
case ZEND_STRLEN:
|
||||
case ZEND_COUNT:
|
||||
case ZEND_GET_TYPE:
|
||||
case ZEND_ISSET_ISEMPTY_THIS:
|
||||
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
|
||||
case ZEND_FETCH_DIM_IS:
|
||||
case ZEND_ISSET_ISEMPTY_CV:
|
||||
case ZEND_ISSET_ISEMPTY_VAR:
|
||||
case ZEND_FETCH_IS:
|
||||
case ZEND_IN_ARRAY:
|
||||
case ZEND_FUNC_NUM_ARGS:
|
||||
case ZEND_FUNC_GET_ARGS:
|
||||
case ZEND_ARRAY_KEY_EXISTS:
|
||||
/* No side effects */
|
||||
return 0;
|
||||
case ZEND_ROPE_END:
|
||||
/* TODO: Rope dce optimization, see #76446 */
|
||||
return 1;
|
||||
case ZEND_JMP:
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_JMP_NULL:
|
||||
/* For our purposes a jumps and branches are side effects. */
|
||||
return 1;
|
||||
case ZEND_BEGIN_SILENCE:
|
||||
case ZEND_END_SILENCE:
|
||||
case ZEND_ECHO:
|
||||
case ZEND_INCLUDE_OR_EVAL:
|
||||
case ZEND_THROW:
|
||||
case ZEND_MATCH_ERROR:
|
||||
case ZEND_EXT_STMT:
|
||||
case ZEND_EXT_FCALL_BEGIN:
|
||||
case ZEND_EXT_FCALL_END:
|
||||
case ZEND_TICKS:
|
||||
case ZEND_YIELD:
|
||||
case ZEND_YIELD_FROM:
|
||||
case ZEND_VERIFY_NEVER_TYPE:
|
||||
/* Intrinsic side effects */
|
||||
return 1;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
/* For now assume all calls have side effects */
|
||||
return 1;
|
||||
case ZEND_RECV:
|
||||
case ZEND_RECV_INIT:
|
||||
/* Even though RECV_INIT can be side-effect free, these cannot be simply dropped
|
||||
* due to the prologue skipping code. */
|
||||
return 1;
|
||||
case ZEND_ASSIGN_REF:
|
||||
return 1;
|
||||
case ZEND_ASSIGN:
|
||||
{
|
||||
if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)) {
|
||||
return 1;
|
||||
}
|
||||
if (!reorder_dtor_effects) {
|
||||
if (opline->op2_type != IS_CONST
|
||||
&& (OP2_INFO() & MAY_HAVE_DTOR)
|
||||
&& ssa->vars[ssa_op->op2_use].escape_state != ESCAPE_STATE_NO_ESCAPE) {
|
||||
/* DCE might shorten lifetime */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case ZEND_UNSET_VAR:
|
||||
return 1;
|
||||
case ZEND_UNSET_CV:
|
||||
{
|
||||
uint32_t t1 = OP1_INFO();
|
||||
if (t1 & MAY_BE_REF) {
|
||||
/* We don't consider uses as the LHS of an assignment as real uses during DCE, so
|
||||
* an unset may be considered dead even if there is a later assignment to the
|
||||
* variable. Removing the unset in this case would not be correct if the variable
|
||||
* is a reference, because unset breaks references. */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_POST_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_POST_DEC:
|
||||
return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def);
|
||||
case ZEND_ASSIGN_OP:
|
||||
return is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
|
||||
|| ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE;
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
|
||||
|| ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
|
||||
return 1;
|
||||
}
|
||||
if (!reorder_dtor_effects) {
|
||||
opline++;
|
||||
ssa_op++;
|
||||
if (opline->op1_type != IS_CONST
|
||||
&& (OP1_INFO() & MAY_HAVE_DTOR)) {
|
||||
/* DCE might shorten lifetime */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
if (is_bad_mod(ssa, ssa_op->op1_use, ssa_op->op1_def)
|
||||
|| ssa->vars[ssa_op->op1_def].escape_state != ESCAPE_STATE_NO_ESCAPE) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case ZEND_BIND_STATIC:
|
||||
if (op_array->static_variables) {
|
||||
/* Implicit and Explicit bind static is effectively prologue of closure so
|
||||
report it has side effects like RECV, RECV_INIT; This allows us to
|
||||
reflect on the closure and discover used variable at runtime */
|
||||
if ((opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((opline->extended_value & ZEND_BIND_REF) != 0) {
|
||||
zval *value =
|
||||
(zval*)((char*)op_array->static_variables->arData +
|
||||
(opline->extended_value & ~ZEND_BIND_REF));
|
||||
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
|
||||
/* AST may contain undefined constants */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case ZEND_CHECK_VAR:
|
||||
return (OP1_INFO() & MAY_BE_UNDEF) != 0;
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
/* Model as not having side-effects -- let the side-effect be introduced by
|
||||
* FE_FETCH if the array is not known to be non-empty. */
|
||||
return (OP1_INFO() & MAY_BE_ANY) != MAY_BE_ARRAY;
|
||||
default:
|
||||
/* For everything we didn't handle, assume a side-effect */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline void add_to_worklists(context *ctx, int var_num, int check) {
|
||||
zend_ssa_var *var = &ctx->ssa->vars[var_num];
|
||||
if (var->definition >= 0) {
|
||||
if (!check || zend_bitset_in(ctx->instr_dead, var->definition)) {
|
||||
zend_bitset_incl(ctx->instr_worklist, var->definition);
|
||||
}
|
||||
} else if (var->definition_phi) {
|
||||
if (!check || zend_bitset_in(ctx->phi_dead, var_num)) {
|
||||
zend_bitset_incl(ctx->phi_worklist, var_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void add_to_phi_worklist_no_val(context *ctx, int var_num) {
|
||||
zend_ssa_var *var = &ctx->ssa->vars[var_num];
|
||||
if (var->definition_phi && zend_bitset_in(ctx->phi_dead, var_num)) {
|
||||
zend_bitset_incl(ctx->phi_worklist_no_val, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline void add_operands_to_worklists(context *ctx, zend_op *opline, zend_ssa_op *ssa_op, zend_ssa *ssa, int check) {
|
||||
if (ssa_op->result_use >= 0) {
|
||||
add_to_worklists(ctx, ssa_op->result_use, check);
|
||||
}
|
||||
if (ssa_op->op1_use >= 0) {
|
||||
if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op1_use)
|
||||
|| (opline->opcode == ZEND_ASSIGN
|
||||
&& (ssa->var_info[ssa_op->op1_use].type & MAY_BE_REF) != 0)) {
|
||||
add_to_worklists(ctx, ssa_op->op1_use, check);
|
||||
} else {
|
||||
add_to_phi_worklist_no_val(ctx, ssa_op->op1_use);
|
||||
}
|
||||
}
|
||||
if (ssa_op->op2_use >= 0) {
|
||||
if (!zend_ssa_is_no_val_use(opline, ssa_op, ssa_op->op2_use)
|
||||
|| (opline->opcode == ZEND_FE_FETCH_R
|
||||
&& (ssa->var_info[ssa_op->op2_use].type & MAY_BE_REF) != 0)) {
|
||||
add_to_worklists(ctx, ssa_op->op2_use, check);
|
||||
} else {
|
||||
add_to_phi_worklist_no_val(ctx, ssa_op->op2_use);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline void add_phi_sources_to_worklists(context *ctx, zend_ssa_phi *phi, int check) {
|
||||
zend_ssa *ssa = ctx->ssa;
|
||||
int source;
|
||||
FOREACH_PHI_SOURCE(phi, source) {
|
||||
add_to_worklists(ctx, source, check);
|
||||
} FOREACH_PHI_SOURCE_END();
|
||||
}
|
||||
|
||||
static inline bool is_var_dead(context *ctx, int var_num) {
|
||||
zend_ssa_var *var = &ctx->ssa->vars[var_num];
|
||||
if (var->definition_phi) {
|
||||
return zend_bitset_in(ctx->phi_dead, var_num);
|
||||
} else if (var->definition >= 0) {
|
||||
return zend_bitset_in(ctx->instr_dead, var->definition);
|
||||
} else {
|
||||
/* Variable has no definition, so either the definition has already been removed (var is
|
||||
* dead) or this is one of the implicit variables at the start of the function (for our
|
||||
* purposes live) */
|
||||
return var_num >= ctx->op_array->last_var;
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes we can mark the var as EXT_UNUSED
|
||||
static bool try_remove_var_def(context *ctx, int free_var, int use_chain, zend_op *opline) {
|
||||
if (use_chain >= 0) {
|
||||
return 0;
|
||||
}
|
||||
zend_ssa_var *var = &ctx->ssa->vars[free_var];
|
||||
int def = var->definition;
|
||||
|
||||
if (def >= 0) {
|
||||
zend_ssa_op *def_op = &ctx->ssa->ops[def];
|
||||
|
||||
if (def_op->result_def == free_var
|
||||
&& var->phi_use_chain == NULL
|
||||
&& var->use_chain == (opline - ctx->op_array->opcodes)) {
|
||||
zend_op *def_opline = &ctx->op_array->opcodes[def];
|
||||
|
||||
switch (def_opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
case ZEND_ASSIGN_REF:
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
case ZEND_ASSIGN_OP:
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_INCLUDE_OR_EVAL:
|
||||
case ZEND_YIELD:
|
||||
case ZEND_YIELD_FROM:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
def_opline->result_type = IS_UNUSED;
|
||||
def_opline->result.var = 0;
|
||||
def_op->result_def = -1;
|
||||
var->definition = -1;
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zend_always_inline zend_bool may_be_refcounted(uint32_t type) {
|
||||
return (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_REF)) != 0;
|
||||
}
|
||||
|
||||
static inline bool is_free_of_live_var(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_FREE:
|
||||
/* It is always safe to remove FREEs of non-refcounted values, even if they are live. */
|
||||
if (!may_be_refcounted(ctx->ssa->var_info[ssa_op->op1_use].type)) {
|
||||
return 0;
|
||||
}
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_FE_FREE:
|
||||
return !is_var_dead(ctx, ssa_op->op1_use);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns whether the instruction has been DCEd */
|
||||
static bool dce_instr(context *ctx, zend_op *opline, zend_ssa_op *ssa_op) {
|
||||
zend_ssa *ssa = ctx->ssa;
|
||||
int free_var = -1;
|
||||
zend_uchar free_var_type;
|
||||
|
||||
if (opline->opcode == ZEND_NOP) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We mark FREEs as dead, but they're only really dead if the destroyed var is dead */
|
||||
if (is_free_of_live_var(ctx, opline, ssa_op)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((opline->op1_type & (IS_VAR|IS_TMP_VAR))&& !is_var_dead(ctx, ssa_op->op1_use)) {
|
||||
if (!try_remove_var_def(ctx, ssa_op->op1_use, ssa_op->op1_use_chain, opline)) {
|
||||
if (may_be_refcounted(ssa->var_info[ssa_op->op1_use].type)
|
||||
&& opline->opcode != ZEND_CASE && opline->opcode != ZEND_CASE_STRICT) {
|
||||
free_var = ssa_op->op1_use;
|
||||
free_var_type = opline->op1_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((opline->op2_type & (IS_VAR|IS_TMP_VAR)) && !is_var_dead(ctx, ssa_op->op2_use)) {
|
||||
if (!try_remove_var_def(ctx, ssa_op->op2_use, ssa_op->op2_use_chain, opline)) {
|
||||
if (may_be_refcounted(ssa->var_info[ssa_op->op2_use].type)) {
|
||||
if (free_var >= 0) {
|
||||
// TODO: We can't free two vars. Keep instruction alive.
|
||||
zend_bitset_excl(ctx->instr_dead, opline - ctx->op_array->opcodes);
|
||||
return 0;
|
||||
}
|
||||
free_var = ssa_op->op2_use;
|
||||
free_var_type = opline->op2_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zend_ssa_rename_defs_of_instr(ctx->ssa, ssa_op);
|
||||
zend_ssa_remove_instr(ctx->ssa, opline, ssa_op);
|
||||
|
||||
if (free_var >= 0) {
|
||||
opline->opcode = ZEND_FREE;
|
||||
opline->op1.var = EX_NUM_TO_VAR(ssa->vars[free_var].var);
|
||||
opline->op1_type = free_var_type;
|
||||
|
||||
ssa_op->op1_use = free_var;
|
||||
ssa_op->op1_use_chain = ssa->vars[free_var].use_chain;
|
||||
ssa->vars[free_var].use_chain = ssa_op - ssa->ops;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int get_common_phi_source(zend_ssa *ssa, zend_ssa_phi *phi) {
|
||||
int common_source = -1;
|
||||
int source;
|
||||
FOREACH_PHI_SOURCE(phi, source) {
|
||||
if (common_source == -1) {
|
||||
common_source = source;
|
||||
} else if (common_source != source && source != phi->ssa_var) {
|
||||
return -1;
|
||||
}
|
||||
} FOREACH_PHI_SOURCE_END();
|
||||
ZEND_ASSERT(common_source != -1);
|
||||
return common_source;
|
||||
}
|
||||
|
||||
static void try_remove_trivial_phi(context *ctx, zend_ssa_phi *phi) {
|
||||
zend_ssa *ssa = ctx->ssa;
|
||||
if (phi->pi < 0) {
|
||||
/* Phi assignment with identical source operands */
|
||||
int common_source = get_common_phi_source(ssa, phi);
|
||||
if (common_source >= 0) {
|
||||
zend_ssa_rename_var_uses(ssa, phi->ssa_var, common_source, 1);
|
||||
zend_ssa_remove_phi(ssa, phi);
|
||||
}
|
||||
} else {
|
||||
/* Pi assignment that is only used in Phi/Pi assignments */
|
||||
// TODO What if we want to rerun type inference after DCE? Maybe separate this?
|
||||
/*ZEND_ASSERT(phi->sources[0] != -1);
|
||||
if (ssa->vars[phi->ssa_var].use_chain < 0) {
|
||||
zend_ssa_rename_var_uses_keep_types(ssa, phi->ssa_var, phi->sources[0], 1);
|
||||
zend_ssa_remove_phi(ssa, phi);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool may_break_varargs(const zend_op_array *op_array, const zend_ssa *ssa, const zend_ssa_op *ssa_op) {
|
||||
if (ssa_op->op1_def >= 0
|
||||
&& ssa->vars[ssa_op->op1_def].var < op_array->num_args) {
|
||||
return 1;
|
||||
}
|
||||
if (ssa_op->op2_def >= 0
|
||||
&& ssa->vars[ssa_op->op2_def].var < op_array->num_args) {
|
||||
return 1;
|
||||
}
|
||||
if (ssa_op->result_def >= 0
|
||||
&& ssa->vars[ssa_op->result_def].var < op_array->num_args) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_dtor_effects) {
|
||||
int i;
|
||||
zend_ssa_phi *phi;
|
||||
int removed_ops = 0;
|
||||
|
||||
/* DCE of CV operations that changes arguments may affect vararg functions. */
|
||||
bool has_varargs = (ssa->cfg.flags & ZEND_FUNC_VARARG) != 0;
|
||||
|
||||
context ctx;
|
||||
ctx.ssa = ssa;
|
||||
ctx.op_array = op_array;
|
||||
ctx.reorder_dtor_effects = reorder_dtor_effects;
|
||||
|
||||
/* We have no dedicated phi vector, so we use the whole ssa var vector instead */
|
||||
ctx.instr_worklist_len = zend_bitset_len(op_array->last);
|
||||
ctx.instr_worklist = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
memset(ctx.instr_worklist, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
ctx.phi_worklist_len = zend_bitset_len(ssa->vars_count);
|
||||
ctx.phi_worklist = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
memset(ctx.phi_worklist, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
ctx.phi_worklist_no_val = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
memset(ctx.phi_worklist_no_val, 0, sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
|
||||
/* Optimistically assume all instructions and phis to be dead */
|
||||
ctx.instr_dead = alloca(sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
memset(ctx.instr_dead, 0, sizeof(zend_ulong) * ctx.instr_worklist_len);
|
||||
ctx.phi_dead = alloca(sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
memset(ctx.phi_dead, 0xff, sizeof(zend_ulong) * ctx.phi_worklist_len);
|
||||
|
||||
/* Mark non-CV phis as live. Even if the result is unused, we generally cannot remove one
|
||||
* of the producing instructions, as it combines producing the result with control flow.
|
||||
* This can be made more precise if there are any cases where this is not the case. */
|
||||
FOREACH_PHI(phi) {
|
||||
if (phi->var >= op_array->last_var
|
||||
&& may_be_refcounted(ssa->var_info[phi->ssa_var].type)) {
|
||||
zend_bitset_excl(ctx.phi_dead, phi->ssa_var);
|
||||
add_phi_sources_to_worklists(&ctx, phi, 0);
|
||||
}
|
||||
} FOREACH_PHI_END();
|
||||
|
||||
/* Mark reachable instruction without side effects as dead */
|
||||
int b = ssa->cfg.blocks_count;
|
||||
while (b > 0) {
|
||||
int op_data = -1;
|
||||
|
||||
b--;
|
||||
zend_basic_block *block = &ssa->cfg.blocks[b];
|
||||
if (!(block->flags & ZEND_BB_REACHABLE)) {
|
||||
continue;
|
||||
}
|
||||
i = block->start + block->len;
|
||||
while (i > block->start) {
|
||||
i--;
|
||||
|
||||
if (op_array->opcodes[i].opcode == ZEND_OP_DATA) {
|
||||
op_data = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zend_bitset_in(ctx.instr_worklist, i)) {
|
||||
zend_bitset_excl(ctx.instr_worklist, i);
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0);
|
||||
if (op_data >= 0) {
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0);
|
||||
}
|
||||
} else if (may_have_side_effects(op_array, ssa, &op_array->opcodes[i], &ssa->ops[i], ctx.reorder_dtor_effects)
|
||||
|| zend_may_throw(&op_array->opcodes[i], &ssa->ops[i], op_array, ssa)
|
||||
|| (has_varargs && may_break_varargs(op_array, ssa, &ssa->ops[i]))) {
|
||||
if (op_array->opcodes[i].opcode == ZEND_NEW
|
||||
&& op_array->opcodes[i+1].opcode == ZEND_DO_FCALL
|
||||
&& ssa->ops[i].result_def >= 0
|
||||
&& ssa->vars[ssa->ops[i].result_def].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
zend_bitset_incl(ctx.instr_dead, i);
|
||||
zend_bitset_incl(ctx.instr_dead, i+1);
|
||||
} else {
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 0);
|
||||
if (op_data >= 0) {
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[op_data], &ssa->ops[op_data], ssa, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zend_bitset_incl(ctx.instr_dead, i);
|
||||
if (op_data >= 0) {
|
||||
zend_bitset_incl(ctx.instr_dead, op_data);
|
||||
}
|
||||
}
|
||||
op_data = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Propagate liveness backwards to all definitions of used vars */
|
||||
while (!zend_bitset_empty(ctx.instr_worklist, ctx.instr_worklist_len)
|
||||
|| !zend_bitset_empty(ctx.phi_worklist, ctx.phi_worklist_len)) {
|
||||
while ((i = zend_bitset_pop_first(ctx.instr_worklist, ctx.instr_worklist_len)) >= 0) {
|
||||
zend_bitset_excl(ctx.instr_dead, i);
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[i], &ssa->ops[i], ssa, 1);
|
||||
if (i < op_array->last && op_array->opcodes[i+1].opcode == ZEND_OP_DATA) {
|
||||
zend_bitset_excl(ctx.instr_dead, i+1);
|
||||
add_operands_to_worklists(&ctx, &op_array->opcodes[i+1], &ssa->ops[i+1], ssa, 1);
|
||||
}
|
||||
}
|
||||
while ((i = zend_bitset_pop_first(ctx.phi_worklist, ctx.phi_worklist_len)) >= 0) {
|
||||
zend_bitset_excl(ctx.phi_dead, i);
|
||||
zend_bitset_excl(ctx.phi_worklist_no_val, i);
|
||||
add_phi_sources_to_worklists(&ctx, ssa->vars[i].definition_phi, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Eliminate dead instructions */
|
||||
ZEND_BITSET_FOREACH(ctx.instr_dead, ctx.instr_worklist_len, i) {
|
||||
removed_ops += dce_instr(&ctx, &op_array->opcodes[i], &ssa->ops[i]);
|
||||
} ZEND_BITSET_FOREACH_END();
|
||||
|
||||
/* Improper uses don't count as "uses" for the purpose of instruction elimination,
|
||||
* but we have to retain phis defining them.
|
||||
* Propagate this information backwards, marking any phi with an improperly used
|
||||
* target as non-dead. */
|
||||
while ((i = zend_bitset_pop_first(ctx.phi_worklist_no_val, ctx.phi_worklist_len)) >= 0) {
|
||||
zend_ssa_phi *phi = ssa->vars[i].definition_phi;
|
||||
int source;
|
||||
zend_bitset_excl(ctx.phi_dead, i);
|
||||
FOREACH_PHI_SOURCE(phi, source) {
|
||||
add_to_phi_worklist_no_val(&ctx, source);
|
||||
} FOREACH_PHI_SOURCE_END();
|
||||
}
|
||||
|
||||
/* Now collect the actually dead phis */
|
||||
FOREACH_PHI(phi) {
|
||||
if (zend_bitset_in(ctx.phi_dead, phi->ssa_var)) {
|
||||
zend_ssa_remove_uses_of_var(ssa, phi->ssa_var);
|
||||
zend_ssa_remove_phi(ssa, phi);
|
||||
} else {
|
||||
/* Remove trivial phis (phis with identical source operands) */
|
||||
try_remove_trivial_phi(&ctx, phi);
|
||||
}
|
||||
} FOREACH_PHI_END();
|
||||
|
||||
return removed_ops;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,538 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache, Escape Analysis |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_bitset.h"
|
||||
#include "zend_cfg.h"
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_inference.h"
|
||||
#include "zend_dump.h"
|
||||
|
||||
/*
|
||||
* T. Kotzmann and H. Mossenbock. Escape analysis in the context of dynamic
|
||||
* compilation and deoptimization. In Proceedings of the International
|
||||
* Conference on Virtual Execution Environments, pages 111-120, Chicago,
|
||||
* June 2005
|
||||
*/
|
||||
|
||||
static zend_always_inline void union_find_init(int *parent, int *size, int count) /* {{{ */
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
parent[i] = i;
|
||||
size[i] = 1;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_always_inline int union_find_root(int *parent, int i) /* {{{ */
|
||||
{
|
||||
int p = parent[i];
|
||||
|
||||
while (i != p) {
|
||||
p = parent[p];
|
||||
parent[i] = p;
|
||||
i = p;
|
||||
p = parent[i];
|
||||
}
|
||||
return i;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_always_inline void union_find_unite(int *parent, int *size, int i, int j) /* {{{ */
|
||||
{
|
||||
int r1 = union_find_root(parent, i);
|
||||
int r2 = union_find_root(parent, j);
|
||||
|
||||
if (r1 != r2) {
|
||||
if (size[r1] < size[r2]) {
|
||||
parent[r1] = r2;
|
||||
size[r2] += size[r1];
|
||||
} else {
|
||||
parent[r2] = r1;
|
||||
size[r1] += size[r2];
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int zend_build_equi_escape_sets(int *parent, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
|
||||
{
|
||||
zend_ssa_var *ssa_vars = ssa->vars;
|
||||
int ssa_vars_count = ssa->vars_count;
|
||||
zend_ssa_phi *p;
|
||||
int i, j;
|
||||
int *size;
|
||||
ALLOCA_FLAG(use_heap)
|
||||
|
||||
size = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
|
||||
if (!size) {
|
||||
return FAILURE;
|
||||
}
|
||||
union_find_init(parent, size, ssa_vars_count);
|
||||
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
if (ssa_vars[i].definition_phi) {
|
||||
p = ssa_vars[i].definition_phi;
|
||||
if (p->pi >= 0) {
|
||||
union_find_unite(parent, size, i, p->sources[0]);
|
||||
} else {
|
||||
for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
|
||||
union_find_unite(parent, size, i, p->sources[j]);
|
||||
}
|
||||
}
|
||||
} else if (ssa_vars[i].definition >= 0) {
|
||||
int def = ssa_vars[i].definition;
|
||||
zend_ssa_op *op = ssa->ops + def;
|
||||
zend_op *opline = op_array->opcodes + def;
|
||||
|
||||
if (op->op1_def >= 0) {
|
||||
if (op->op1_use >= 0) {
|
||||
if (opline->opcode != ZEND_ASSIGN) {
|
||||
union_find_unite(parent, size, op->op1_def, op->op1_use);
|
||||
}
|
||||
}
|
||||
if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
|
||||
union_find_unite(parent, size, op->op1_def, op->op2_use);
|
||||
}
|
||||
}
|
||||
if (op->op2_def >= 0) {
|
||||
if (op->op2_use >= 0) {
|
||||
union_find_unite(parent, size, op->op2_def, op->op2_use);
|
||||
}
|
||||
}
|
||||
if (op->result_def >= 0) {
|
||||
if (op->result_use >= 0) {
|
||||
if (opline->opcode != ZEND_QM_ASSIGN) {
|
||||
union_find_unite(parent, size, op->result_def, op->result_use);
|
||||
}
|
||||
}
|
||||
if (opline->opcode == ZEND_QM_ASSIGN && op->op1_use >= 0) {
|
||||
union_find_unite(parent, size, op->result_def, op->op1_use);
|
||||
}
|
||||
if (opline->opcode == ZEND_ASSIGN && op->op2_use >= 0) {
|
||||
union_find_unite(parent, size, op->result_def, op->op2_use);
|
||||
}
|
||||
if (opline->opcode == ZEND_ASSIGN && op->op1_def >= 0) {
|
||||
union_find_unite(parent, size, op->result_def, op->op1_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
parent[i] = union_find_root(parent, i);
|
||||
}
|
||||
|
||||
free_alloca(size, use_heap);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static inline zend_class_entry *get_class_entry(const zend_script *script, zend_string *lcname) /* {{{ */
|
||||
{
|
||||
zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL;
|
||||
if (ce) {
|
||||
return ce;
|
||||
}
|
||||
|
||||
ce = zend_hash_find_ptr(CG(class_table), lcname);
|
||||
if (ce && ce->type == ZEND_INTERNAL_CLASS) {
|
||||
return ce;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
|
||||
{
|
||||
zend_ssa_op *ssa_op = ssa->ops + def;
|
||||
zend_op *opline = op_array->opcodes + def;
|
||||
|
||||
if (ssa_op->result_def == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_ARRAY:
|
||||
return 1;
|
||||
case ZEND_NEW:
|
||||
/* objects with destructors should escape */
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1));
|
||||
uint32_t forbidden_flags =
|
||||
/* These flags will always cause an exception */
|
||||
ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
|
||||
| ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT;
|
||||
if (ce && !ce->parent && !ce->create_object && !ce->constructor &&
|
||||
!ce->destructor && !ce->__get && !ce->__set &&
|
||||
!(ce->ce_flags & forbidden_flags) &&
|
||||
(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_QM_ASSIGN:
|
||||
if (opline->op1_type == IS_CONST
|
||||
&& Z_TYPE_P(CRT_CONSTANT(opline->op1)) == IS_ARRAY) {
|
||||
return 1;
|
||||
}
|
||||
if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN:
|
||||
if (opline->op1_type == IS_CV && (OP1_INFO() & MAY_BE_ARRAY)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (ssa_op->op1_def == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if (opline->op2_type == IS_CONST
|
||||
&& Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_ARRAY) {
|
||||
return 1;
|
||||
}
|
||||
if (opline->op2_type == IS_CV && (OP2_INFO() & MAY_BE_ARRAY)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM:
|
||||
if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) {
|
||||
/* implicit object/array allocation */
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var, const zend_script *script) /* {{{ */
|
||||
{
|
||||
zend_ssa_op *op = ssa->ops + def;
|
||||
zend_op *opline = op_array->opcodes + def;
|
||||
|
||||
if (op->result_def == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_ARRAY:
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_ASSIGN:
|
||||
return 1;
|
||||
case ZEND_NEW:
|
||||
/* objects with destructors should escape */
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zend_class_entry *ce = get_class_entry(script, Z_STR_P(CRT_CONSTANT(opline->op1)+1));
|
||||
if (ce && !ce->create_object && !ce->constructor &&
|
||||
!ce->destructor && !ce->__get && !ce->__set && !ce->parent) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (op->op1_def == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int var) /* {{{ */
|
||||
{
|
||||
zend_ssa_op *ssa_op = ssa->ops + use;
|
||||
zend_op *opline = op_array->opcodes + use;
|
||||
|
||||
if (ssa_op->op1_use == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
/* no_val */
|
||||
break;
|
||||
case ZEND_QM_ASSIGN:
|
||||
if (opline->op1_type == IS_CV) {
|
||||
if (OP1_INFO() & MAY_BE_OBJECT) {
|
||||
/* object aliasing */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ISSET_ISEMPTY_DIM_OBJ:
|
||||
case ZEND_ISSET_ISEMPTY_PROP_OBJ:
|
||||
case ZEND_FETCH_DIM_R:
|
||||
case ZEND_FETCH_OBJ_R:
|
||||
case ZEND_FETCH_DIM_IS:
|
||||
case ZEND_FETCH_OBJ_IS:
|
||||
break;
|
||||
case ZEND_ASSIGN_OP:
|
||||
return 1;
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
break;
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
break;
|
||||
case ZEND_INIT_ARRAY:
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
if (opline->extended_value & ZEND_ARRAY_ELEMENT_REF) {
|
||||
return 1;
|
||||
}
|
||||
if (OP1_INFO() & MAY_BE_OBJECT) {
|
||||
/* object aliasing */
|
||||
return 1;
|
||||
}
|
||||
/* reference dependencies processed separately */
|
||||
break;
|
||||
case ZEND_OP_DATA:
|
||||
if ((opline-1)->opcode != ZEND_ASSIGN_DIM
|
||||
&& (opline-1)->opcode != ZEND_ASSIGN_OBJ) {
|
||||
return 1;
|
||||
}
|
||||
if (OP1_INFO() & MAY_BE_OBJECT) {
|
||||
/* object aliasing */
|
||||
return 1;
|
||||
}
|
||||
opline--;
|
||||
ssa_op--;
|
||||
if (opline->op1_type != IS_CV
|
||||
|| (OP1_INFO() & MAY_BE_REF)
|
||||
|| (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
|
||||
/* assignment into escaping structure */
|
||||
return 1;
|
||||
}
|
||||
/* reference dependencies processed separately */
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssa_op->op2_use == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if (opline->op1_type != IS_CV
|
||||
|| (OP1_INFO() & MAY_BE_REF)
|
||||
|| (ssa_op->op1_def >= 0 && ssa->vars[ssa_op->op1_def].alias)) {
|
||||
/* assignment into escaping variable */
|
||||
return 1;
|
||||
}
|
||||
if (opline->op2_type == IS_CV || opline->result_type != IS_UNUSED) {
|
||||
if (OP2_INFO() & MAY_BE_OBJECT) {
|
||||
/* object aliasing */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssa_op->result_use == var) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_INIT_ARRAY:
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa) /* {{{ */
|
||||
{
|
||||
zend_ssa_var *ssa_vars = ssa->vars;
|
||||
int ssa_vars_count = ssa->vars_count;
|
||||
int i, root, use;
|
||||
int *ees;
|
||||
bool has_allocations;
|
||||
int num_non_escaped;
|
||||
ALLOCA_FLAG(use_heap)
|
||||
|
||||
if (!ssa_vars) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
has_allocations = 0;
|
||||
for (i = op_array->last_var; i < ssa_vars_count; i++) {
|
||||
if (ssa_vars[i].definition >= 0
|
||||
&& (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))
|
||||
&& is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
|
||||
has_allocations = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_allocations) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* 1. Build EES (Equi-Escape Sets) */
|
||||
ees = do_alloca(sizeof(int) * ssa_vars_count, use_heap);
|
||||
if (!ees) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (zend_build_equi_escape_sets(ees, op_array, ssa) != SUCCESS) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* 2. Identify Allocations */
|
||||
num_non_escaped = 0;
|
||||
for (i = op_array->last_var; i < ssa_vars_count; i++) {
|
||||
root = ees[i];
|
||||
if (ssa_vars[root].escape_state > ESCAPE_STATE_NO_ESCAPE) {
|
||||
/* already escape. skip */
|
||||
} else if (ssa_vars[i].alias && (ssa->var_info[i].type & MAY_BE_REF)) {
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
num_non_escaped--;
|
||||
}
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
|
||||
} else if (ssa_vars[i].definition >= 0
|
||||
&& (ssa->var_info[i].type & (MAY_BE_ARRAY|MAY_BE_OBJECT))) {
|
||||
if (!is_local_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
num_non_escaped--;
|
||||
}
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
|
||||
} else if (ssa_vars[root].escape_state == ESCAPE_STATE_UNKNOWN
|
||||
&& is_allocation_def(op_array, ssa, ssa_vars[i].definition, i, script)) {
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_NO_ESCAPE;
|
||||
num_non_escaped++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. Mark escaped EES */
|
||||
if (num_non_escaped) {
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
if (ssa_vars[i].use_chain >= 0) {
|
||||
root = ees[i];
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
FOREACH_USE(ssa_vars + i, use) {
|
||||
if (is_escape_use(op_array, ssa, use, i)) {
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
|
||||
num_non_escaped--;
|
||||
if (num_non_escaped == 0) {
|
||||
i = ssa_vars_count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} FOREACH_USE_END();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 4. Process referential dependencies */
|
||||
if (num_non_escaped) {
|
||||
bool changed;
|
||||
|
||||
do {
|
||||
changed = 0;
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
if (ssa_vars[i].use_chain >= 0) {
|
||||
root = ees[i];
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_NO_ESCAPE) {
|
||||
FOREACH_USE(ssa_vars + i, use) {
|
||||
zend_ssa_op *op = ssa->ops + use;
|
||||
zend_op *opline = op_array->opcodes + use;
|
||||
int enclosing_root;
|
||||
|
||||
if (opline->opcode == ZEND_OP_DATA &&
|
||||
((opline-1)->opcode == ZEND_ASSIGN_DIM ||
|
||||
(opline-1)->opcode == ZEND_ASSIGN_OBJ ||
|
||||
(opline-1)->opcode == ZEND_ASSIGN_OBJ_REF) &&
|
||||
op->op1_use == i &&
|
||||
(op-1)->op1_use >= 0) {
|
||||
enclosing_root = ees[(op-1)->op1_use];
|
||||
} else if ((opline->opcode == ZEND_INIT_ARRAY ||
|
||||
opline->opcode == ZEND_ADD_ARRAY_ELEMENT) &&
|
||||
op->op1_use == i &&
|
||||
op->result_def >= 0) {
|
||||
enclosing_root = ees[op->result_def];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN ||
|
||||
ssa_vars[enclosing_root].escape_state > ssa_vars[root].escape_state) {
|
||||
if (ssa_vars[enclosing_root].escape_state == ESCAPE_STATE_UNKNOWN) {
|
||||
ssa_vars[root].escape_state = ESCAPE_STATE_GLOBAL_ESCAPE;
|
||||
} else {
|
||||
ssa_vars[root].escape_state = ssa_vars[enclosing_root].escape_state;
|
||||
}
|
||||
if (ssa_vars[root].escape_state == ESCAPE_STATE_GLOBAL_ESCAPE) {
|
||||
num_non_escaped--;
|
||||
if (num_non_escaped == 0) {
|
||||
changed = 0;
|
||||
} else {
|
||||
changed = 1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
} FOREACH_USE_END();
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
/* 5. Propagate values of escape sets to variables */
|
||||
for (i = 0; i < ssa_vars_count; i++) {
|
||||
root = ees[i];
|
||||
if (i != root) {
|
||||
ssa_vars[i].escape_state = ssa_vars[root].escape_state;
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(ees, use_heap);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 10:
|
||||
* - remove NOPs
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *end, *opline;
|
||||
uint32_t new_count, i, shift;
|
||||
int j;
|
||||
uint32_t *shiftlist;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
|
||||
shiftlist = (uint32_t *)do_alloca(sizeof(uint32_t) * op_array->last, use_heap);
|
||||
i = new_count = shift = 0;
|
||||
end = op_array->opcodes + op_array->last;
|
||||
for (opline = op_array->opcodes; opline < end; opline++) {
|
||||
|
||||
/* Kill JMP-over-NOP-s */
|
||||
if (opline->opcode == ZEND_JMP && ZEND_OP1_JMP_ADDR(opline) > op_array->opcodes + i) {
|
||||
/* check if there are only NOPs under the branch */
|
||||
zend_op *target = ZEND_OP1_JMP_ADDR(opline) - 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) {
|
||||
zend_op *new_opline = op_array->opcodes + new_count;
|
||||
|
||||
*new_opline = *opline;
|
||||
zend_optimizer_migrate_jump(op_array, new_opline, opline);
|
||||
}
|
||||
new_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (shift) {
|
||||
op_array->last = new_count;
|
||||
end = op_array->opcodes + op_array->last;
|
||||
|
||||
/* update JMPs */
|
||||
for (opline = op_array->opcodes; opline<end; opline++) {
|
||||
zend_optimizer_shift_jump(op_array, opline, shiftlist);
|
||||
}
|
||||
|
||||
/* 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 (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];
|
||||
}
|
||||
}
|
||||
|
||||
/* update early binding list */
|
||||
if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
|
||||
uint32_t *opline_num = &ctx->script->first_early_binding_opline;
|
||||
|
||||
ZEND_ASSERT(op_array == &ctx->script->main_op_array);
|
||||
do {
|
||||
*opline_num -= shiftlist[*opline_num];
|
||||
opline_num = &op_array->opcodes[*opline_num].result.opline_num;
|
||||
} while (*opline_num != (uint32_t)-1);
|
||||
}
|
||||
}
|
||||
free_alloca(shiftlist, use_heap);
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
| Xinchen Hui <laruence@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 4
|
||||
* - optimize INIT_FCALL_BY_NAME to DO_FCALL
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
#define ZEND_OP1_IS_CONST_STRING(opline) \
|
||||
(opline->op1_type == IS_CONST && \
|
||||
Z_TYPE(op_array->literals[(opline)->op1.constant]) == IS_STRING)
|
||||
#define ZEND_OP2_IS_CONST_STRING(opline) \
|
||||
(opline->op2_type == IS_CONST && \
|
||||
Z_TYPE(op_array->literals[(opline)->op2.constant]) == IS_STRING)
|
||||
|
||||
typedef struct _optimizer_call_info {
|
||||
zend_function *func;
|
||||
zend_op *opline;
|
||||
bool is_prototype;
|
||||
bool try_inline;
|
||||
uint32_t func_arg_num;
|
||||
} optimizer_call_info;
|
||||
|
||||
static void zend_delete_call_instructions(zend_op *opline)
|
||||
{
|
||||
int call = 0;
|
||||
|
||||
while (1) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_FCALL:
|
||||
if (call == 0) {
|
||||
MAKE_NOP(opline);
|
||||
return;
|
||||
}
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_NEW:
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
call--;
|
||||
break;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
call++;
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
if (call == 0) {
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
MAKE_NOP(opline);
|
||||
} else if (opline->op1_type == IS_CV) {
|
||||
opline->opcode = ZEND_CHECK_VAR;
|
||||
opline->extended_value = 0;
|
||||
opline->result.var = 0;
|
||||
} else {
|
||||
opline->opcode = ZEND_FREE;
|
||||
opline->extended_value = 0;
|
||||
opline->result.var = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
opline--;
|
||||
}
|
||||
}
|
||||
|
||||
static void zend_try_inline_call(zend_op_array *op_array, zend_op *fcall, zend_op *opline, zend_function *func)
|
||||
{
|
||||
if (func->type == ZEND_USER_FUNCTION
|
||||
&& !(func->op_array.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_HAS_TYPE_HINTS))
|
||||
/* TODO: function copied from trait may be inconsistent ??? */
|
||||
&& !(func->op_array.fn_flags & (ZEND_ACC_TRAIT_CLONE))
|
||||
&& fcall->extended_value >= func->op_array.required_num_args
|
||||
&& func->op_array.opcodes[func->op_array.num_args].opcode == ZEND_RETURN) {
|
||||
|
||||
zend_op *ret_opline = func->op_array.opcodes + func->op_array.num_args;
|
||||
|
||||
if (ret_opline->op1_type == IS_CONST) {
|
||||
uint32_t i, num_args = func->op_array.num_args;
|
||||
num_args += (func->op_array.fn_flags & ZEND_ACC_VARIADIC) != 0;
|
||||
|
||||
if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
|
||||
&& !(func->op_array.fn_flags & ZEND_ACC_STATIC)) {
|
||||
/* Don't inline static call to instance method. */
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_args; i++) {
|
||||
/* Don't inline functions with by-reference arguments. This would require
|
||||
* correct handling of INDIRECT arguments. */
|
||||
if (ZEND_ARG_SEND_MODE(&func->op_array.arg_info[i])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fcall->extended_value < func->op_array.num_args) {
|
||||
/* don't inline functions with named constants in default arguments */
|
||||
i = fcall->extended_value;
|
||||
|
||||
do {
|
||||
if (Z_TYPE_P(CRT_CONSTANT_EX(&func->op_array, &func->op_array.opcodes[i], func->op_array.opcodes[i].op2)) == IS_CONSTANT_AST) {
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
} while (i < func->op_array.num_args);
|
||||
}
|
||||
|
||||
if (RETURN_VALUE_USED(opline)) {
|
||||
zval zv;
|
||||
|
||||
ZVAL_COPY(&zv, CRT_CONSTANT_EX(&func->op_array, ret_opline, ret_opline->op1));
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->op1_type = IS_CONST;
|
||||
opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
|
||||
SET_UNUSED(opline->op2);
|
||||
} else {
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
|
||||
zend_delete_call_instructions(opline-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *opline = op_array->opcodes;
|
||||
zend_op *end = opline + op_array->last;
|
||||
int call = 0;
|
||||
void *checkpoint;
|
||||
optimizer_call_info *call_stack;
|
||||
|
||||
if (op_array->last < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkpoint = zend_arena_checkpoint(ctx->arena);
|
||||
call_stack = zend_arena_calloc(&ctx->arena, op_array->last / 2, sizeof(optimizer_call_info));
|
||||
while (opline < end) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_NEW:
|
||||
/* The argument passing optimizations are valid for prototypes as well,
|
||||
* as inheritance cannot change between ref <-> non-ref arguments. */
|
||||
call_stack[call].func = zend_optimizer_get_called_func(
|
||||
ctx->script, op_array, opline, &call_stack[call].is_prototype);
|
||||
call_stack[call].try_inline =
|
||||
!call_stack[call].is_prototype && opline->opcode != ZEND_NEW;
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
call_stack[call].opline = opline;
|
||||
call_stack[call].func_arg_num = (uint32_t)-1;
|
||||
call++;
|
||||
break;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
call--;
|
||||
if (call_stack[call].func && call_stack[call].opline) {
|
||||
zend_op *fcall = call_stack[call].opline;
|
||||
|
||||
if (fcall->opcode == ZEND_INIT_FCALL) {
|
||||
/* nothing to do */
|
||||
} else if (fcall->opcode == ZEND_INIT_FCALL_BY_NAME) {
|
||||
fcall->opcode = ZEND_INIT_FCALL;
|
||||
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
|
||||
literal_dtor(&ZEND_OP2_LITERAL(fcall));
|
||||
fcall->op2.constant = fcall->op2.constant + 1;
|
||||
opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
|
||||
} else if (fcall->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
|
||||
fcall->opcode = ZEND_INIT_FCALL;
|
||||
fcall->op1.num = zend_vm_calc_used_stack(fcall->extended_value, call_stack[call].func);
|
||||
literal_dtor(&op_array->literals[fcall->op2.constant]);
|
||||
literal_dtor(&op_array->literals[fcall->op2.constant + 2]);
|
||||
fcall->op2.constant = fcall->op2.constant + 1;
|
||||
opline->opcode = zend_get_call_op(fcall, call_stack[call].func);
|
||||
} else if (fcall->opcode == ZEND_INIT_STATIC_METHOD_CALL
|
||||
|| fcall->opcode == ZEND_INIT_METHOD_CALL
|
||||
|| fcall->opcode == ZEND_NEW) {
|
||||
/* We don't have specialized opcodes for this, do nothing */
|
||||
} else {
|
||||
ZEND_UNREACHABLE();
|
||||
}
|
||||
|
||||
if ((ZEND_OPTIMIZER_PASS_16 & ctx->optimization_level)
|
||||
&& call_stack[call].try_inline) {
|
||||
zend_try_inline_call(op_array, fcall, opline, call_stack[call].func);
|
||||
}
|
||||
}
|
||||
call_stack[call].func = NULL;
|
||||
call_stack[call].opline = NULL;
|
||||
call_stack[call].try_inline = 0;
|
||||
call_stack[call].func_arg_num = (uint32_t)-1;
|
||||
break;
|
||||
case ZEND_FETCH_FUNC_ARG:
|
||||
case ZEND_FETCH_STATIC_PROP_FUNC_ARG:
|
||||
case ZEND_FETCH_OBJ_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
if (call_stack[call - 1].func
|
||||
&& call_stack[call - 1].func_arg_num != (uint32_t)-1) {
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, call_stack[call - 1].func_arg_num)) {
|
||||
if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
|
||||
opline->opcode -= 9;
|
||||
} else {
|
||||
opline->opcode = ZEND_FETCH_STATIC_PROP_W;
|
||||
}
|
||||
} else {
|
||||
if (opline->opcode == ZEND_FETCH_DIM_FUNC_ARG
|
||||
&& opline->op2_type == IS_UNUSED) {
|
||||
/* FETCH_DIM_FUNC_ARG supports UNUSED op2, while FETCH_DIM_R does not.
|
||||
* Performing the replacement would create an invalid opcode. */
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (opline->opcode != ZEND_FETCH_STATIC_PROP_FUNC_ARG) {
|
||||
opline->opcode -= 12;
|
||||
} else {
|
||||
opline->opcode = ZEND_FETCH_STATIC_PROP_R;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAL_EX:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
/* We won't convert it into_DO_FCALL to emit error at run-time */
|
||||
call_stack[call - 1].opline = NULL;
|
||||
} else {
|
||||
opline->opcode = ZEND_SEND_VAL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_CHECK_FUNC_ARG:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
call_stack[call - 1].func_arg_num = (uint32_t)-1;
|
||||
break;
|
||||
}
|
||||
|
||||
call_stack[call - 1].func_arg_num = opline->op2.num;
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
call_stack[call - 1].func_arg_num = (uint32_t)-1;
|
||||
if (ARG_SHOULD_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
opline->opcode = ZEND_SEND_REF;
|
||||
} else {
|
||||
opline->opcode = ZEND_SEND_VAR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
if (call_stack[call - 1].func) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARG_MUST_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
opline->opcode = ZEND_SEND_VAR_NO_REF;
|
||||
} else if (ARG_MAY_BE_SENT_BY_REF(call_stack[call - 1].func, opline->op2.num)) {
|
||||
opline->opcode = ZEND_SEND_VAL;
|
||||
} else {
|
||||
opline->opcode = ZEND_SEND_VAR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_REF:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_SEND_USER:
|
||||
case ZEND_SEND_ARRAY:
|
||||
call_stack[call - 1].try_inline = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
|
||||
zend_arena_release(&ctx->arena, checkpoint);
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
#define GET_AVAILABLE_T() \
|
||||
for (i = 0; i < T; i++) { \
|
||||
if (!zend_bitset_in(taken_T, i)) { \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
zend_bitset_incl(taken_T, i); \
|
||||
if (i > max) { \
|
||||
max = i; \
|
||||
}
|
||||
|
||||
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
int T = op_array->T;
|
||||
int offset = op_array->last_var;
|
||||
uint32_t bitset_len;
|
||||
zend_bitset taken_T; /* T index in use */
|
||||
zend_op **start_of_T; /* opline where T is first used */
|
||||
zend_bitset 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;
|
||||
void *checkpoint = zend_arena_checkpoint(ctx->arena);
|
||||
|
||||
bitset_len = zend_bitset_len(T);
|
||||
taken_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
|
||||
start_of_T = (zend_op **) zend_arena_alloc(&ctx->arena, T * sizeof(zend_op *));
|
||||
valid_T = (zend_bitset) zend_arena_alloc(&ctx->arena, bitset_len * ZEND_BITSET_ELM_SIZE);
|
||||
map_T = (int *) zend_arena_alloc(&ctx->arena, T * sizeof(int));
|
||||
|
||||
end = op_array->opcodes;
|
||||
opline = &op_array->opcodes[op_array->last - 1];
|
||||
|
||||
/* Find T definition points */
|
||||
while (opline >= end) {
|
||||
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
|
||||
start_of_T[VAR_NUM(opline->result.var) - offset] = opline;
|
||||
}
|
||||
opline--;
|
||||
}
|
||||
|
||||
zend_bitset_clear(valid_T, bitset_len);
|
||||
zend_bitset_clear(taken_T, bitset_len);
|
||||
|
||||
end = op_array->opcodes;
|
||||
opline = &op_array->opcodes[op_array->last - 1];
|
||||
|
||||
while (opline >= end) {
|
||||
if ((opline->op1_type & (IS_VAR | IS_TMP_VAR))) {
|
||||
currT = VAR_NUM(opline->op1.var) - offset;
|
||||
if (opline->opcode == ZEND_ROPE_END) {
|
||||
int num = (((opline->extended_value + 1) * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
|
||||
int var;
|
||||
|
||||
var = max;
|
||||
while (var >= 0 && !zend_bitset_in(taken_T, var)) {
|
||||
var--;
|
||||
}
|
||||
max = MAX(max, var + num);
|
||||
var = var + 1;
|
||||
map_T[currT] = var;
|
||||
zend_bitset_incl(valid_T, currT);
|
||||
zend_bitset_incl(taken_T, var);
|
||||
opline->op1.var = NUM_VAR(var + offset);
|
||||
while (num > 1) {
|
||||
num--;
|
||||
zend_bitset_incl(taken_T, var + num);
|
||||
}
|
||||
} else {
|
||||
if (!zend_bitset_in(valid_T, currT)) {
|
||||
int use_new_var = 0;
|
||||
|
||||
/* Code in "finally" blocks may modify temporary variables.
|
||||
* We allocate new temporaries for values that need to
|
||||
* relive FAST_CALLs.
|
||||
*/
|
||||
if ((op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK) &&
|
||||
(opline->opcode == ZEND_RETURN ||
|
||||
opline->opcode == ZEND_GENERATOR_RETURN ||
|
||||
opline->opcode == ZEND_RETURN_BY_REF ||
|
||||
opline->opcode == ZEND_FREE ||
|
||||
opline->opcode == ZEND_FE_FREE)) {
|
||||
zend_op *curr = opline;
|
||||
|
||||
while (--curr >= end) {
|
||||
if (curr->opcode == ZEND_FAST_CALL) {
|
||||
use_new_var = 1;
|
||||
break;
|
||||
} else if (curr->opcode != ZEND_FREE &&
|
||||
curr->opcode != ZEND_FE_FREE &&
|
||||
curr->opcode != ZEND_VERIFY_RETURN_TYPE &&
|
||||
curr->opcode != ZEND_DISCARD_EXCEPTION) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (use_new_var) {
|
||||
i = ++max;
|
||||
zend_bitset_incl(taken_T, i);
|
||||
} else {
|
||||
GET_AVAILABLE_T();
|
||||
}
|
||||
map_T[currT] = i;
|
||||
zend_bitset_incl(valid_T, currT);
|
||||
}
|
||||
opline->op1.var = NUM_VAR(map_T[currT] + offset);
|
||||
}
|
||||
}
|
||||
|
||||
if ((opline->op2_type & (IS_VAR | IS_TMP_VAR))) {
|
||||
currT = VAR_NUM(opline->op2.var) - offset;
|
||||
if (!zend_bitset_in(valid_T, currT)) {
|
||||
GET_AVAILABLE_T();
|
||||
map_T[currT] = i;
|
||||
zend_bitset_incl(valid_T, currT);
|
||||
}
|
||||
opline->op2.var = NUM_VAR(map_T[currT] + offset);
|
||||
}
|
||||
|
||||
if (opline->result_type & (IS_VAR | IS_TMP_VAR)) {
|
||||
currT = VAR_NUM(opline->result.var) - offset;
|
||||
if (zend_bitset_in(valid_T, currT)) {
|
||||
if (start_of_T[currT] == opline) {
|
||||
/* ZEND_FAST_CALL can not share temporary var with others
|
||||
* since the fast_var could also be set by ZEND_HANDLE_EXCEPTION
|
||||
* which could be ahead of it */
|
||||
if (opline->opcode != ZEND_FAST_CALL) {
|
||||
zend_bitset_excl(taken_T, map_T[currT]);
|
||||
}
|
||||
}
|
||||
opline->result.var = NUM_VAR(map_T[currT] + offset);
|
||||
if (opline->opcode == ZEND_ROPE_INIT) {
|
||||
if (start_of_T[currT] == opline) {
|
||||
uint32_t num = ((opline->extended_value * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval);
|
||||
while (num > 1) {
|
||||
num--;
|
||||
zend_bitset_excl(taken_T, map_T[currT]+num);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Code which gets here is using a wrongly built opcode such as RECV() */
|
||||
GET_AVAILABLE_T();
|
||||
map_T[currT] = i;
|
||||
zend_bitset_incl(valid_T, currT);
|
||||
opline->result.var = NUM_VAR(i + offset);
|
||||
}
|
||||
}
|
||||
|
||||
opline--;
|
||||
}
|
||||
|
||||
zend_arena_release(&ctx->arena, checkpoint);
|
||||
op_array->T = max + 1;
|
||||
}
|
||||
@@ -1,685 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 1 (Simple local optimizations)
|
||||
* - persistent constant substitution (true, false, null, etc)
|
||||
* - constant casting (ADD expects numbers, CONCAT strings, etc)
|
||||
* - constant expression evaluation
|
||||
* - optimize constant conditional JMPs
|
||||
* - pre-evaluate constant function calls
|
||||
* - eliminate FETCH $GLOBALS followed by FETCH_DIM/UNSET_DIM/ISSET_ISEMPTY_DIM
|
||||
*/
|
||||
|
||||
#include "php.h"
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *opline = op_array->opcodes;
|
||||
zend_op *end = opline + op_array->last;
|
||||
bool collect_constants = (ZEND_OPTIMIZER_PASS_15 & ctx->optimization_level)?
|
||||
(op_array == &ctx->script->main_op_array) : 0;
|
||||
|
||||
while (opline < end) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_CONCAT:
|
||||
case ZEND_FAST_CONCAT:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
if (Z_TYPE(ZEND_OP1_LITERAL(opline)) != IS_STRING) {
|
||||
convert_to_string(&ZEND_OP1_LITERAL(opline));
|
||||
}
|
||||
}
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
|
||||
convert_to_string(&ZEND_OP2_LITERAL(opline));
|
||||
}
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
goto constant_binary_op;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_ADD:
|
||||
case ZEND_SUB:
|
||||
case ZEND_MUL:
|
||||
case ZEND_DIV:
|
||||
case ZEND_POW:
|
||||
case ZEND_MOD:
|
||||
case ZEND_SL:
|
||||
case ZEND_SR:
|
||||
case ZEND_BW_OR:
|
||||
case ZEND_BW_AND:
|
||||
case ZEND_BW_XOR:
|
||||
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_BOOL_XOR:
|
||||
case ZEND_SPACESHIP:
|
||||
case ZEND_CASE:
|
||||
case ZEND_CASE_STRICT:
|
||||
if (opline->op1_type == IS_CONST &&
|
||||
opline->op2_type == IS_CONST) {
|
||||
/* binary operation with constant operands */
|
||||
zval result;
|
||||
|
||||
constant_binary_op:
|
||||
if (zend_optimizer_eval_binary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline)) == SUCCESS) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
literal_dtor(&ZEND_OP2_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &result);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_ASSIGN_OP:
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
if (opline->extended_value == ZEND_ADD
|
||||
|| opline->extended_value == ZEND_SUB
|
||||
|| opline->extended_value == ZEND_MUL
|
||||
|| opline->extended_value == ZEND_DIV
|
||||
|| opline->extended_value == ZEND_POW) {
|
||||
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
|
||||
/* don't optimize if it should produce a runtime numeric string error */
|
||||
if (is_numeric_string(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), NULL, NULL, 0)) {
|
||||
convert_scalar_to_number(&ZEND_OP2_LITERAL(opline));
|
||||
}
|
||||
}
|
||||
} else if (opline->extended_value == ZEND_MOD
|
||||
|| opline->extended_value == ZEND_SL
|
||||
|| opline->extended_value == ZEND_SR) {
|
||||
zval *op2 = &ZEND_OP2_LITERAL(opline);
|
||||
if (Z_TYPE_P(op2) != IS_LONG) {
|
||||
if (!zend_is_op_long_compatible(op2)) {
|
||||
break;
|
||||
}
|
||||
convert_to_long(op2);
|
||||
}
|
||||
} else if (opline->extended_value == ZEND_CONCAT) {
|
||||
if (Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_STRING) {
|
||||
convert_to_string(&ZEND_OP2_LITERAL(opline));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_CAST:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
/* cast of constant operand */
|
||||
zval result;
|
||||
|
||||
if (zend_optimizer_eval_cast(&result, opline->extended_value, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, opline->result_type, opline->result.var, &result)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
zend_optimizer_update_op1_const(op_array, opline, &result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_BW_NOT:
|
||||
case ZEND_BOOL_NOT:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
/* unary operation on constant operand */
|
||||
zval result;
|
||||
|
||||
if (zend_optimizer_eval_unary_op(&result, opline->opcode, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &result)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zend_optimizer_update_op1_const(op_array, opline, &result);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_FETCH_CONSTANT:
|
||||
if (opline->op2_type == 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_execute_data *orig_execute_data = EG(current_execute_data);
|
||||
zend_execute_data fake_execute_data;
|
||||
zval *offset;
|
||||
|
||||
memset(&fake_execute_data, 0, sizeof(zend_execute_data));
|
||||
fake_execute_data.func = (zend_function*)op_array;
|
||||
EG(current_execute_data) = &fake_execute_data;
|
||||
if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
|
||||
|
||||
literal_dtor(&ZEND_OP2_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, offset)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, offset);
|
||||
}
|
||||
}
|
||||
EG(current_execute_data) = orig_execute_data;
|
||||
break;
|
||||
}
|
||||
|
||||
if (opline->op2_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
|
||||
/* substitute persistent constants */
|
||||
zval c;
|
||||
|
||||
if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP2_LITERAL(opline)), &c, 1)) {
|
||||
if (!ctx->constants || !zend_optimizer_get_collected_constant(ctx->constants, &ZEND_OP2_LITERAL(opline), &c)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Z_TYPE(c) == IS_CONSTANT_AST) {
|
||||
break;
|
||||
}
|
||||
literal_dtor(&ZEND_OP2_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_FETCH_CLASS_CONSTANT:
|
||||
if (opline->op2_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING) {
|
||||
|
||||
zend_class_entry *ce = NULL;
|
||||
|
||||
if (opline->op1_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING) {
|
||||
/* for A::B */
|
||||
if (op_array->scope &&
|
||||
zend_string_equals_ci(Z_STR(ZEND_OP1_LITERAL(opline)), op_array->scope->name)) {
|
||||
ce = op_array->scope;
|
||||
} else {
|
||||
if ((ce = zend_hash_find_ptr(EG(class_table),
|
||||
Z_STR(op_array->literals[opline->op1.constant + 1]))) == NULL ||
|
||||
(ce->type == ZEND_INTERNAL_CLASS &&
|
||||
ce->info.internal.module->type != MODULE_PERSISTENT) ||
|
||||
(ce->type == ZEND_USER_CLASS &&
|
||||
ce->info.user.filename != op_array->filename)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (op_array->scope &&
|
||||
opline->op1_type == IS_UNUSED &&
|
||||
(opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) {
|
||||
/* for self::B */
|
||||
ce = op_array->scope;
|
||||
} else if (op_array->scope &&
|
||||
opline->op1_type == IS_VAR &&
|
||||
(opline - 1)->opcode == ZEND_FETCH_CLASS &&
|
||||
((opline - 1)->op2_type == IS_UNUSED &&
|
||||
((opline - 1)->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) &&
|
||||
(opline - 1)->result.var == opline->op1.var) {
|
||||
/* for self::B */
|
||||
ce = op_array->scope;
|
||||
}
|
||||
|
||||
if (ce) {
|
||||
zend_class_constant *cc;
|
||||
zval *c, t;
|
||||
|
||||
if ((cc = zend_hash_find_ptr(&ce->constants_table,
|
||||
Z_STR(ZEND_OP2_LITERAL(opline)))) != NULL &&
|
||||
(ZEND_CLASS_CONST_FLAGS(cc) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
|
||||
c = &cc->value;
|
||||
if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
|
||||
zend_ast *ast = Z_ASTVAL_P(c);
|
||||
if (ast->kind != ZEND_AST_CONSTANT
|
||||
|| !zend_optimizer_get_persistent_constant(zend_ast_get_constant_name(ast), &t, 1)
|
||||
|| Z_TYPE(t) == IS_CONSTANT_AST) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ZVAL_COPY_OR_DUP(&t, c);
|
||||
}
|
||||
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
} else if (opline->op1_type == IS_VAR) {
|
||||
MAKE_NOP((opline - 1));
|
||||
}
|
||||
literal_dtor(&ZEND_OP2_LITERAL(opline));
|
||||
|
||||
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_DO_ICALL: {
|
||||
zend_op *send1_opline = opline - 1;
|
||||
zend_op *send2_opline = NULL;
|
||||
zend_op *init_opline = NULL;
|
||||
|
||||
while (send1_opline->opcode == ZEND_NOP) {
|
||||
send1_opline--;
|
||||
}
|
||||
if (send1_opline->opcode != ZEND_SEND_VAL ||
|
||||
send1_opline->op1_type != IS_CONST) {
|
||||
/* don't collect constants after unknown function call */
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
if (send1_opline->op2.num == 2) {
|
||||
send2_opline = send1_opline;
|
||||
send1_opline--;
|
||||
while (send1_opline->opcode == ZEND_NOP) {
|
||||
send1_opline--;
|
||||
}
|
||||
if (send1_opline->opcode != ZEND_SEND_VAL ||
|
||||
send1_opline->op1_type != IS_CONST) {
|
||||
/* don't collect constants after unknown function call */
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
init_opline = send1_opline - 1;
|
||||
while (init_opline->opcode == ZEND_NOP) {
|
||||
init_opline--;
|
||||
}
|
||||
if (init_opline->opcode != ZEND_INIT_FCALL ||
|
||||
init_opline->op2_type != IS_CONST ||
|
||||
Z_TYPE(ZEND_OP2_LITERAL(init_opline)) != IS_STRING) {
|
||||
/* don't collect constants after unknown function call */
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* define("name", scalar); */
|
||||
if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("define")-1 &&
|
||||
zend_binary_strcasecmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)), Z_STRLEN(ZEND_OP2_LITERAL(init_opline)), "define", sizeof("define")-1) == 0) {
|
||||
|
||||
if (Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING &&
|
||||
send2_opline &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(send2_opline)) <= IS_STRING) {
|
||||
|
||||
if (collect_constants) {
|
||||
zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(send1_opline), &ZEND_OP1_LITERAL(send2_opline));
|
||||
}
|
||||
|
||||
if (RESULT_UNUSED(opline) &&
|
||||
!zend_memnstr(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), "::", sizeof("::") - 1, Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)) + Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
|
||||
|
||||
opline->opcode = ZEND_DECLARE_CONST;
|
||||
opline->op1_type = IS_CONST;
|
||||
opline->op2_type = IS_CONST;
|
||||
opline->result_type = IS_UNUSED;
|
||||
opline->op1.constant = send1_opline->op1.constant;
|
||||
opline->op2.constant = send2_opline->op1.constant;
|
||||
opline->result.num = 0;
|
||||
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
MAKE_NOP(send1_opline);
|
||||
MAKE_NOP(send2_opline);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* pre-evaluate constant functions:
|
||||
constant(x)
|
||||
function_exists(x)
|
||||
is_callable(x)
|
||||
extension_loaded(x)
|
||||
*/
|
||||
if (!send2_opline &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING) {
|
||||
if ((Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("function_exists")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"function_exists", sizeof("function_exists")-1)) ||
|
||||
(Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("is_callable")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"is_callable", sizeof("is_callable")))) {
|
||||
zend_internal_function *func;
|
||||
zend_string *lc_name = zend_string_tolower(
|
||||
Z_STR(ZEND_OP1_LITERAL(send1_opline)));
|
||||
|
||||
if ((func = zend_hash_find_ptr(EG(function_table), lc_name)) != NULL
|
||||
&& func->type == ZEND_INTERNAL_FUNCTION
|
||||
&& func->module->type == MODULE_PERSISTENT
|
||||
#ifdef ZEND_WIN32
|
||||
&& func->module->handle == NULL
|
||||
#endif
|
||||
) {
|
||||
zval t;
|
||||
ZVAL_TRUE(&t);
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
|
||||
MAKE_NOP(send1_opline);
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
}
|
||||
zend_string_release_ex(lc_name, 0);
|
||||
break;
|
||||
} else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("extension_loaded")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"extension_loaded", sizeof("extension_loaded")-1)) {
|
||||
zval t;
|
||||
zend_string *lc_name = zend_string_tolower(
|
||||
Z_STR(ZEND_OP1_LITERAL(send1_opline)));
|
||||
zend_module_entry *m = zend_hash_find_ptr(&module_registry,
|
||||
lc_name);
|
||||
|
||||
zend_string_release_ex(lc_name, 0);
|
||||
if (!m) {
|
||||
if (PG(enable_dl)) {
|
||||
break;
|
||||
} else {
|
||||
ZVAL_FALSE(&t);
|
||||
}
|
||||
} else {
|
||||
if (m->type == MODULE_PERSISTENT
|
||||
#ifdef ZEND_WIN32
|
||||
&& m->handle == NULL
|
||||
#endif
|
||||
) {
|
||||
ZVAL_TRUE(&t);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
|
||||
MAKE_NOP(send1_opline);
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
break;
|
||||
} else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("constant")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"constant", sizeof("constant")-1)) {
|
||||
zval t;
|
||||
|
||||
if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &t, 1)) {
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
|
||||
MAKE_NOP(send1_opline);
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* dirname(IS_CONST/IS_STRING) -> IS_CONST/IS_STRING */
|
||||
} else if (Z_STRLEN(ZEND_OP2_LITERAL(init_opline)) == sizeof("dirname")-1 &&
|
||||
!memcmp(Z_STRVAL(ZEND_OP2_LITERAL(init_opline)),
|
||||
"dirname", sizeof("dirname") - 1) &&
|
||||
IS_ABSOLUTE_PATH(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) {
|
||||
zend_string *dirname = zend_string_init(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)), 0);
|
||||
ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname));
|
||||
if (IS_ABSOLUTE_PATH(ZSTR_VAL(dirname), ZSTR_LEN(dirname))) {
|
||||
zval t;
|
||||
|
||||
ZVAL_STR(&t, dirname);
|
||||
literal_dtor(&ZEND_OP2_LITERAL(init_opline));
|
||||
MAKE_NOP(init_opline);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(send1_opline));
|
||||
MAKE_NOP(send1_opline);
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
opline->extended_value = 0;
|
||||
SET_UNUSED(opline->op2);
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
} else {
|
||||
zend_string_release_ex(dirname, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* don't collect constants after any other function call */
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
case ZEND_STRLEN:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zval t;
|
||||
|
||||
if (zend_optimizer_eval_strlen(&t, &ZEND_OP1_LITERAL(opline)) == SUCCESS) {
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline + 1, IS_TMP_VAR, opline->result.var, &t)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zend_optimizer_update_op1_const(op_array, opline, &t);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_DEFINED:
|
||||
{
|
||||
zval c;
|
||||
if (!zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(opline)), &c, 0)) {
|
||||
break;
|
||||
}
|
||||
ZVAL_TRUE(&c);
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
if (zend_optimizer_replace_by_const(op_array, opline, IS_TMP_VAR, opline->result.var, &c)) {
|
||||
MAKE_NOP(opline);
|
||||
} else {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zend_optimizer_update_op1_const(op_array, opline, &c);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_DECLARE_CONST:
|
||||
if (collect_constants &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline)) <= IS_STRING) {
|
||||
zend_optimizer_collect_constant(ctx, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline));
|
||||
}
|
||||
break;
|
||||
#if 0
|
||||
/* see ext/opcache/tests/bug78961.phpt */
|
||||
// case ZEND_FETCH_R:
|
||||
case ZEND_FETCH_W:
|
||||
// case ZEND_FETCH_RW:
|
||||
case ZEND_FETCH_IS:
|
||||
// case ZEND_FETCH_FUNC_ARG:
|
||||
case ZEND_FETCH_UNSET:
|
||||
/* convert FETCH $GLOBALS (global), FETCH_DIM $x into FETCH $x (global) */
|
||||
if ((opline->extended_value & ZEND_FETCH_GLOBAL) != 0 &&
|
||||
opline->op1_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(opline)) == IS_STRING &&
|
||||
zend_string_equals_literal(Z_STR(ZEND_OP1_LITERAL(opline)), "GLOBALS") &&
|
||||
((opline + 1)->opcode == opline->opcode + 1 ||
|
||||
((opline + 1)->opcode == ZEND_UNSET_DIM &&
|
||||
opline->opcode == ZEND_FETCH_UNSET) ||
|
||||
((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ &&
|
||||
opline->opcode == ZEND_FETCH_IS)) &&
|
||||
(opline + 1)->op1_type == opline->result_type &&
|
||||
(opline + 1)->op1.var == opline->result.var &&
|
||||
((opline + 1)->op2_type != IS_CONST ||
|
||||
Z_TYPE(ZEND_OP2_LITERAL(opline + 1)) < IS_ARRAY)) {
|
||||
|
||||
if ((opline + 1)->opcode == ZEND_UNSET_DIM) {
|
||||
(opline + 1)->opcode = ZEND_UNSET_VAR;
|
||||
(opline + 1)->extended_value = ZEND_FETCH_GLOBAL;
|
||||
} else if ((opline + 1)->opcode == ZEND_ISSET_ISEMPTY_DIM_OBJ) {
|
||||
(opline + 1)->opcode = ZEND_ISSET_ISEMPTY_VAR;
|
||||
(opline + 1)->extended_value |= ZEND_FETCH_GLOBAL;
|
||||
} else {
|
||||
(opline + 1)->opcode = opline->opcode;
|
||||
(opline + 1)->extended_value = ZEND_FETCH_GLOBAL;
|
||||
}
|
||||
(opline + 1)->op1_type = (opline + 1)->op2_type;
|
||||
(opline + 1)->op1 = (opline + 1)->op2;
|
||||
if ((opline + 1)->op1_type == IS_CONST &&
|
||||
Z_TYPE(ZEND_OP1_LITERAL(opline + 1)) != IS_STRING) {
|
||||
|
||||
convert_to_string(&ZEND_OP1_LITERAL(opline + 1));
|
||||
zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline + 1)));
|
||||
}
|
||||
SET_UNUSED((opline + 1)->op2);
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
/* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
|
||||
in case we know it wouldn't jump */
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
|
||||
if (opline->opcode == ZEND_JMPZ_EX) {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
|
||||
ZVAL_TRUE(&ZEND_OP1_LITERAL(opline));
|
||||
opline->op2.num = 0;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (opline->opcode == ZEND_JMPNZ_EX) {
|
||||
opline->opcode = ZEND_QM_ASSIGN;
|
||||
zval_ptr_dtor_nogc(&ZEND_OP1_LITERAL(opline));
|
||||
ZVAL_FALSE(&ZEND_OP1_LITERAL(opline));
|
||||
opline->op2.num = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
collect_constants = 0;
|
||||
break;
|
||||
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
if (opline->op1_type == 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));
|
||||
opline->op1_type = IS_UNUSED;
|
||||
if (should_jmp) {
|
||||
opline->opcode = ZEND_JMP;
|
||||
COPY_NODE(opline->op1, opline->op2);
|
||||
opline->op2.num = 0;
|
||||
} else {
|
||||
MAKE_NOP(opline);
|
||||
break;
|
||||
}
|
||||
}
|
||||
collect_constants = 0;
|
||||
break;
|
||||
|
||||
case ZEND_JMPZNZ:
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zend_op *target_opline;
|
||||
|
||||
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
|
||||
target_opline = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value); /* JMPNZ */
|
||||
} else {
|
||||
target_opline = ZEND_OP2_JMP_ADDR(opline); /* JMPZ */
|
||||
}
|
||||
literal_dtor(&ZEND_OP1_LITERAL(opline));
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target_opline);
|
||||
opline->op1_type = IS_UNUSED;
|
||||
opline->opcode = ZEND_JMP;
|
||||
}
|
||||
collect_constants = 0;
|
||||
break;
|
||||
|
||||
case ZEND_RETURN:
|
||||
case ZEND_RETURN_BY_REF:
|
||||
case ZEND_GENERATOR_RETURN:
|
||||
case ZEND_EXIT:
|
||||
case ZEND_THROW:
|
||||
case ZEND_MATCH_ERROR:
|
||||
case ZEND_CATCH:
|
||||
case ZEND_FAST_CALL:
|
||||
case ZEND_FAST_RET:
|
||||
case ZEND_JMP:
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_JMP_NULL:
|
||||
case ZEND_VERIFY_NEVER_TYPE:
|
||||
collect_constants = 0;
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/* pass 3: (Jump optimization)
|
||||
* - optimize series of JMPs
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_constants.h"
|
||||
#include "zend_execute.h"
|
||||
#include "zend_vm.h"
|
||||
|
||||
/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
|
||||
static zend_always_inline int in_hitlist(zend_op *target, zend_op **jmp_hitlist, int jmp_hitlist_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < jmp_hitlist_count; i++) {
|
||||
if (jmp_hitlist[i] == target) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CHECK_LOOP(target) \
|
||||
if (EXPECTED(!in_hitlist(target, jmp_hitlist, jmp_hitlist_count))) { \
|
||||
jmp_hitlist[jmp_hitlist_count++] = target; \
|
||||
} else { \
|
||||
break; \
|
||||
}
|
||||
|
||||
void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx)
|
||||
{
|
||||
zend_op *opline;
|
||||
zend_op *end;
|
||||
zend_op *target;
|
||||
zend_op **jmp_hitlist;
|
||||
int jmp_hitlist_count;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
|
||||
jmp_hitlist = (zend_op**)do_alloca(sizeof(zend_op*)*op_array->last, use_heap);
|
||||
opline = op_array->opcodes;
|
||||
end = opline + op_array->last;
|
||||
|
||||
while (opline < end) {
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_JMP:
|
||||
jmp_hitlist_count = 0;
|
||||
|
||||
target = ZEND_OP1_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target);
|
||||
}
|
||||
|
||||
if (target == opline + 1) {
|
||||
/* convert L: JMP L+1 to NOP */
|
||||
MAKE_NOP(opline);
|
||||
} else if (target->opcode == ZEND_JMPZNZ) {
|
||||
/* JMP L, L: JMPZNZ L1,L2 -> JMPZNZ L1,L2 */
|
||||
*opline = *target;
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zval zv;
|
||||
ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline));
|
||||
opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
|
||||
}
|
||||
goto optimize_jmpznz;
|
||||
} else if ((target->opcode == ZEND_RETURN ||
|
||||
target->opcode == ZEND_RETURN_BY_REF ||
|
||||
target->opcode == ZEND_GENERATOR_RETURN ||
|
||||
target->opcode == ZEND_EXIT) &&
|
||||
!(op_array->fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
|
||||
/* JMP L, L: RETURN to immediate RETURN */
|
||||
*opline = *target;
|
||||
if (opline->op1_type == IS_CONST) {
|
||||
zval zv;
|
||||
ZVAL_COPY(&zv, &ZEND_OP1_LITERAL(opline));
|
||||
opline->op1.constant = zend_optimizer_add_literal(op_array, &zv);
|
||||
}
|
||||
} else if (opline > op_array->opcodes &&
|
||||
((opline-1)->opcode == ZEND_JMPZ ||
|
||||
(opline-1)->opcode == ZEND_JMPNZ)) {
|
||||
if (ZEND_OP2_JMP_ADDR(opline-1) == target) {
|
||||
/* JMPZ(X,L1), JMP(L1) -> NOP, JMP(L1) */
|
||||
if ((opline-1)->op1_type == IS_CV) {
|
||||
(opline-1)->opcode = ZEND_CHECK_VAR;
|
||||
(opline-1)->op2.num = 0;
|
||||
} else if ((opline-1)->op1_type & (IS_TMP_VAR|IS_VAR)) {
|
||||
(opline-1)->opcode = ZEND_FREE;
|
||||
(opline-1)->op2.num = 0;
|
||||
} else {
|
||||
MAKE_NOP(opline-1);
|
||||
}
|
||||
} else {
|
||||
/* JMPZ(X,L1), JMP(L2) -> JMPZNZ(X,L1,L2) */
|
||||
if ((opline-1)->opcode == ZEND_JMPZ) {
|
||||
(opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), target);
|
||||
} else {
|
||||
(opline-1)->extended_value = ZEND_OPLINE_TO_OFFSET((opline-1), ZEND_OP2_JMP_ADDR(opline-1));
|
||||
ZEND_SET_OP_JMP_ADDR((opline-1), (opline-1)->op2, target);
|
||||
}
|
||||
(opline-1)->opcode = ZEND_JMPZNZ;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
jmp_hitlist_count = 0;
|
||||
|
||||
target = ZEND_OP2_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
jmp_hitlist_count = 0;
|
||||
|
||||
target = ZEND_OP2_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* plain JMP */
|
||||
/* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == opline->opcode &&
|
||||
SAME_VAR(opline->op1, target->op1)) {
|
||||
/* same opcode and same var as this opcode */
|
||||
/* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == INV_COND(opline->opcode) &&
|
||||
SAME_VAR(opline->op1, target->op1)) {
|
||||
/* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to
|
||||
JMPZ(X,L1+1) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == ZEND_JMPZNZ &&
|
||||
SAME_VAR(opline->op1, target->op1)) {
|
||||
target = (opline->opcode == ZEND_JMPZ) ?
|
||||
ZEND_OP2_JMP_ADDR(target) :
|
||||
ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
|
||||
}
|
||||
|
||||
/* convert L: JMPZ L+1 to NOP */
|
||||
if (target == opline + 1) {
|
||||
if (opline->op1_type == IS_CV) {
|
||||
opline->opcode = ZEND_CHECK_VAR;
|
||||
opline->op2.num = 0;
|
||||
} else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) {
|
||||
opline->opcode = ZEND_FREE;
|
||||
opline->op2.num = 0;
|
||||
} else {
|
||||
MAKE_NOP(opline);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
jmp_hitlist_count = 0;
|
||||
|
||||
target = ZEND_OP2_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* plain JMP */
|
||||
/* JMPZ_EX(X,L1), L1: JMP(L2) => JMPZ_EX(X,L2), L1: JMP(L2) */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == opline->opcode-3 &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert T=JMPZ_EX(X,L1), L1: JMPZ(T,L2) to
|
||||
JMPZ_EX(X,L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == opline->opcode &&
|
||||
target->result.var == opline->result.var &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert T=JMPZ_EX(X,L1), L1: T=JMPZ_EX(T,L2) to
|
||||
JMPZ_EX(X,L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_JMPZNZ &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* Check for JMPZNZ with same cond variable */
|
||||
target = (opline->opcode == ZEND_JMPZ_EX) ?
|
||||
ZEND_OP2_JMP_ADDR(target) :
|
||||
ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == INV_EX_COND(opline->opcode) &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert T=JMPZ_EX(X,L1), L1: JMPNZ(T,L2) to
|
||||
JMPZ_EX(X,L1+1) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == INV_EX_COND_EX(opline->opcode) &&
|
||||
target->result.var == opline->result.var &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert T=JMPZ_EX(X,L1), L1: T=JMPNZ_EX(T,L2) to
|
||||
JMPZ_EX(X,L1+1) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == ZEND_BOOL &&
|
||||
(SAME_VAR(target->op1, opline->result) ||
|
||||
SAME_VAR(target->op1, opline->op1))) {
|
||||
/* convert Y = JMPZ_EX(X,L1), L1: Z = BOOL(Y) to
|
||||
Z = JMPZ_EX(X,L1+1) */
|
||||
|
||||
/* NOTE: This optimization pattern is not safe, but works, */
|
||||
/* because result of JMPZ_EX instruction */
|
||||
/* is not used on the following path and */
|
||||
/* should be used once on the branch path. */
|
||||
/* */
|
||||
/* The pattern works well only if jumps processed in */
|
||||
/* direct order, otherwise it breaks JMPZ_EX */
|
||||
/* sequences too early. */
|
||||
opline->result.var = target->result.var;
|
||||
target = target + 1;
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
|
||||
}
|
||||
|
||||
/* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
|
||||
if (target == opline + 1) {
|
||||
opline->opcode = ZEND_BOOL;
|
||||
opline->op2.num = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZEND_JMPZNZ:
|
||||
optimize_jmpznz:
|
||||
jmp_hitlist_count = 0;
|
||||
target = ZEND_OP2_JMP_ADDR(opline);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if ((target->opcode == ZEND_JMPZ || target->opcode == ZEND_JMPZNZ) &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_JMPNZ &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op2, target);
|
||||
}
|
||||
|
||||
jmp_hitlist_count = 0;
|
||||
target = ZEND_OFFSET_TO_OPLINE(opline, opline->extended_value);
|
||||
while (1) {
|
||||
if (target->opcode == ZEND_JMP) {
|
||||
/* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */
|
||||
target = ZEND_OP1_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_JMPNZ &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: X = JMPNZ(X, L3) -> JMPZNZ(X, L1+1, L2) */
|
||||
target = ZEND_OP2_JMP_ADDR(target);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_JMPZ &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
|
||||
target = target + 1;
|
||||
} else if (target->opcode == ZEND_JMPZNZ &&
|
||||
SAME_VAR(target->op1, opline->op1)) {
|
||||
/* JMPZNZ(X, L1, L2), L1: JMPZ(X, L3) -> JMPZNZ(X, L3, L2) */
|
||||
target = ZEND_OFFSET_TO_OPLINE(target, target->extended_value);
|
||||
CHECK_LOOP(target);
|
||||
} else if (target->opcode == ZEND_NOP) {
|
||||
target = target + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
opline->extended_value = ZEND_OPLINE_TO_OFFSET(opline, target);
|
||||
}
|
||||
|
||||
if (ZEND_OP2_JMP_ADDR(opline) == target &&
|
||||
!(opline->op1_type & (IS_VAR|IS_TMP_VAR))) {
|
||||
/* JMPZNZ(?,L,L) -> JMP(L) */
|
||||
opline->opcode = ZEND_JMP;
|
||||
ZEND_SET_OP_JMP_ADDR(opline, opline->op1, target);
|
||||
SET_UNUSED(opline->op1);
|
||||
SET_UNUSED(opline->op2);
|
||||
opline->extended_value = 0;
|
||||
}
|
||||
/* Don't convert JMPZNZ back to JMPZ/JMPNZ, because the
|
||||
following JMP is not removed yet. */
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
free_alloca(jmp_hitlist, use_heap);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,229 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Sparse Conditional Data Flow Propagation Framework |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Nikita Popov <nikic@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
#include "Optimizer/scdf.h"
|
||||
|
||||
/* This defines a generic framework for sparse conditional dataflow propagation. The algorithm is
|
||||
* based on "Sparse conditional constant propagation" by Wegman and Zadeck. We're using a
|
||||
* generalized implementation as described in chapter 8.3 of the SSA book.
|
||||
*
|
||||
* Every SSA variable is associated with an element on a finite-height lattice, those value can only
|
||||
* ever be lowered during the operation of the algorithm. If a value is lowered all instructions and
|
||||
* phis using that value need to be reconsidered (this is done by adding the variable to a
|
||||
* worklist). For phi functions the result is computed by applying the meet operation to the
|
||||
* operands. This continues until a fixed point is reached.
|
||||
*
|
||||
* The algorithm is control-flow sensitive: All blocks except the start block are initially assumed
|
||||
* to be unreachable. When considering a branch instruction, we determine the feasible successors
|
||||
* based on the current state of the variable lattice. If a new edge becomes feasible we either have
|
||||
* to mark the successor block executable and consider all instructions in it, or, if the target is
|
||||
* already executable, we only have to reconsider the phi functions (as we only consider phi
|
||||
* operands which are associated with a feasible edge).
|
||||
*
|
||||
* The generic framework requires the definition of three functions:
|
||||
* * visit_instr() should recompute the lattice values of all SSA variables defined by an
|
||||
* instruction.
|
||||
* * visit_phi() should recompute the lattice value of the SSA variable defined by the phi. While
|
||||
* doing this it should only consider operands for which scfg_is_edge_feasible() returns true.
|
||||
* * get_feasible_successors() should determine the feasible successors for a branch instruction.
|
||||
* Note that this callback only needs to handle conditional branches (with two successors).
|
||||
*/
|
||||
|
||||
#if 0
|
||||
#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINT(...)
|
||||
#endif
|
||||
|
||||
void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to) {
|
||||
uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
|
||||
|
||||
if (zend_bitset_in(scdf->feasible_edges, edge)) {
|
||||
/* We already handled this edge */
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_PRINT("Marking edge %d->%d feasible\n", from, to);
|
||||
zend_bitset_incl(scdf->feasible_edges, edge);
|
||||
|
||||
if (!zend_bitset_in(scdf->executable_blocks, to)) {
|
||||
if (!zend_bitset_in(scdf->block_worklist, to)) {
|
||||
DEBUG_PRINT("Adding block %d to worklist\n", to);
|
||||
}
|
||||
zend_bitset_incl(scdf->block_worklist, to);
|
||||
} else {
|
||||
/* Block is already executable, only a new edge became feasible.
|
||||
* Reevaluate phi nodes to account for changed source operands. */
|
||||
zend_ssa_block *ssa_block = &scdf->ssa->blocks[to];
|
||||
zend_ssa_phi *phi;
|
||||
for (phi = ssa_block->phis; phi; phi = phi->next) {
|
||||
zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
|
||||
scdf->handlers.visit_phi(scdf, phi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa) {
|
||||
scdf->op_array = op_array;
|
||||
scdf->ssa = ssa;
|
||||
|
||||
scdf->instr_worklist_len = zend_bitset_len(op_array->last);
|
||||
scdf->phi_var_worklist_len = zend_bitset_len(ssa->vars_count);
|
||||
scdf->block_worklist_len = zend_bitset_len(ssa->cfg.blocks_count);
|
||||
|
||||
scdf->instr_worklist = zend_arena_calloc(&ctx->arena,
|
||||
scdf->instr_worklist_len + scdf->phi_var_worklist_len + 2 * scdf->block_worklist_len + zend_bitset_len(ssa->cfg.edges_count),
|
||||
sizeof(zend_ulong));
|
||||
|
||||
scdf->phi_var_worklist = scdf->instr_worklist + scdf->instr_worklist_len;
|
||||
scdf->block_worklist = scdf->phi_var_worklist + scdf->phi_var_worklist_len;
|
||||
scdf->executable_blocks = scdf->block_worklist + scdf->block_worklist_len;
|
||||
scdf->feasible_edges = scdf->executable_blocks + scdf->block_worklist_len;
|
||||
|
||||
zend_bitset_incl(scdf->block_worklist, 0);
|
||||
zend_bitset_incl(scdf->executable_blocks, 0);
|
||||
}
|
||||
|
||||
void scdf_solve(scdf_ctx *scdf, const char *name) {
|
||||
zend_ssa *ssa = scdf->ssa;
|
||||
DEBUG_PRINT("Start SCDF solve (%s)\n", name);
|
||||
while (!zend_bitset_empty(scdf->instr_worklist, scdf->instr_worklist_len)
|
||||
|| !zend_bitset_empty(scdf->phi_var_worklist, scdf->phi_var_worklist_len)
|
||||
|| !zend_bitset_empty(scdf->block_worklist, scdf->block_worklist_len)
|
||||
) {
|
||||
int i;
|
||||
while ((i = zend_bitset_pop_first(scdf->phi_var_worklist, scdf->phi_var_worklist_len)) >= 0) {
|
||||
zend_ssa_phi *phi = ssa->vars[i].definition_phi;
|
||||
ZEND_ASSERT(phi);
|
||||
if (zend_bitset_in(scdf->executable_blocks, phi->block)) {
|
||||
scdf->handlers.visit_phi(scdf, phi);
|
||||
}
|
||||
}
|
||||
|
||||
while ((i = zend_bitset_pop_first(scdf->instr_worklist, scdf->instr_worklist_len)) >= 0) {
|
||||
int block_num = ssa->cfg.map[i];
|
||||
if (zend_bitset_in(scdf->executable_blocks, block_num)) {
|
||||
zend_basic_block *block = &ssa->cfg.blocks[block_num];
|
||||
zend_op *opline = &scdf->op_array->opcodes[i];
|
||||
zend_ssa_op *ssa_op = &ssa->ops[i];
|
||||
if (opline->opcode == ZEND_OP_DATA) {
|
||||
opline--;
|
||||
ssa_op--;
|
||||
}
|
||||
scdf->handlers.visit_instr(scdf, opline, ssa_op);
|
||||
if (i == block->start + block->len - 1) {
|
||||
if (block->successors_count == 1) {
|
||||
scdf_mark_edge_feasible(scdf, block_num, block->successors[0]);
|
||||
} else if (block->successors_count > 1) {
|
||||
scdf->handlers.mark_feasible_successors(scdf, block_num, block, opline, ssa_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while ((i = zend_bitset_pop_first(scdf->block_worklist, scdf->block_worklist_len)) >= 0) {
|
||||
/* This block is now live. Interpret phis and instructions in it. */
|
||||
zend_basic_block *block = &ssa->cfg.blocks[i];
|
||||
zend_ssa_block *ssa_block = &ssa->blocks[i];
|
||||
|
||||
DEBUG_PRINT("Pop block %d from worklist\n", i);
|
||||
zend_bitset_incl(scdf->executable_blocks, i);
|
||||
|
||||
{
|
||||
zend_ssa_phi *phi;
|
||||
for (phi = ssa_block->phis; phi; phi = phi->next) {
|
||||
zend_bitset_excl(scdf->phi_var_worklist, phi->ssa_var);
|
||||
scdf->handlers.visit_phi(scdf, phi);
|
||||
}
|
||||
}
|
||||
|
||||
if (block->len == 0) {
|
||||
/* Zero length blocks don't have a last instruction that would normally do this */
|
||||
scdf_mark_edge_feasible(scdf, i, block->successors[0]);
|
||||
} else {
|
||||
zend_op *opline = NULL;
|
||||
int j, end = block->start + block->len;
|
||||
for (j = block->start; j < end; j++) {
|
||||
opline = &scdf->op_array->opcodes[j];
|
||||
zend_bitset_excl(scdf->instr_worklist, j);
|
||||
if (opline->opcode != ZEND_OP_DATA) {
|
||||
scdf->handlers.visit_instr(scdf, opline, &ssa->ops[j]);
|
||||
}
|
||||
}
|
||||
if (block->successors_count == 1) {
|
||||
scdf_mark_edge_feasible(scdf, i, block->successors[0]);
|
||||
} else if (block->successors_count > 1) {
|
||||
ZEND_ASSERT(opline && "Should have opline in non-empty block");
|
||||
if (opline->opcode == ZEND_OP_DATA) {
|
||||
opline--;
|
||||
j--;
|
||||
}
|
||||
scdf->handlers.mark_feasible_successors(scdf, i, block, opline, &ssa->ops[j-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If a live range starts in a reachable block and ends in an unreachable block, we should
|
||||
* not eliminate the latter. While it cannot be reached, the FREE opcode of the loop var
|
||||
* is necessary for the correctness of temporary compaction. */
|
||||
static bool kept_alive_by_loop_var_free(scdf_ctx *scdf, uint32_t block_idx) {
|
||||
uint32_t i;
|
||||
const zend_op_array *op_array = scdf->op_array;
|
||||
const zend_cfg *cfg = &scdf->ssa->cfg;
|
||||
const zend_basic_block *block = &cfg->blocks[block_idx];
|
||||
if (!(cfg->flags & ZEND_FUNC_FREE_LOOP_VAR)) {
|
||||
return 0;
|
||||
}
|
||||
for (i = block->start; i < block->start + block->len; i++) {
|
||||
zend_op *opline = &op_array->opcodes[i];
|
||||
if (zend_optimizer_is_loop_var_free(opline)) {
|
||||
int ssa_var = scdf->ssa->ops[i].op1_use;
|
||||
if (ssa_var >= 0) {
|
||||
int op_num = scdf->ssa->vars[ssa_var].definition;
|
||||
uint32_t def_block;
|
||||
ZEND_ASSERT(op_num >= 0);
|
||||
def_block = cfg->map[op_num];
|
||||
if (zend_bitset_in(scdf->executable_blocks, def_block)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Removes unreachable blocks. This will remove both the instructions (and phis) in the
|
||||
* blocks, as well as remove them from the successor / predecessor lists and mark them
|
||||
* unreachable. Blocks already marked unreachable are not removed. */
|
||||
int scdf_remove_unreachable_blocks(scdf_ctx *scdf) {
|
||||
zend_ssa *ssa = scdf->ssa;
|
||||
int i;
|
||||
int removed_ops = 0;
|
||||
for (i = 0; i < ssa->cfg.blocks_count; i++) {
|
||||
if (!zend_bitset_in(scdf->executable_blocks, i)
|
||||
&& (ssa->cfg.blocks[i].flags & ZEND_BB_REACHABLE)
|
||||
&& !kept_alive_by_loop_var_free(scdf, i)) {
|
||||
removed_ops += ssa->cfg.blocks[i].len;
|
||||
zend_ssa_remove_block(scdf->op_array, ssa, i);
|
||||
}
|
||||
}
|
||||
return removed_ops;
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Call Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Nikita Popov <nikic@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef _SCDF_H
|
||||
#define _SCDF_H
|
||||
|
||||
#include "zend_bitset.h"
|
||||
|
||||
typedef struct _scdf_ctx {
|
||||
zend_op_array *op_array;
|
||||
zend_ssa *ssa;
|
||||
zend_bitset instr_worklist;
|
||||
/* Represent phi-instructions through the defining var */
|
||||
zend_bitset phi_var_worklist;
|
||||
zend_bitset block_worklist;
|
||||
zend_bitset executable_blocks;
|
||||
/* 1 bit per edge, see scdf_edge(cfg, from, to) */
|
||||
zend_bitset feasible_edges;
|
||||
uint32_t instr_worklist_len;
|
||||
uint32_t phi_var_worklist_len;
|
||||
uint32_t block_worklist_len;
|
||||
|
||||
struct {
|
||||
void (*visit_instr)(
|
||||
struct _scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_op);
|
||||
void (*visit_phi)(
|
||||
struct _scdf_ctx *scdf, zend_ssa_phi *phi);
|
||||
void (*mark_feasible_successors)(
|
||||
struct _scdf_ctx *scdf, int block_num, zend_basic_block *block,
|
||||
zend_op *opline, zend_ssa_op *ssa_op);
|
||||
} handlers;
|
||||
} scdf_ctx;
|
||||
|
||||
void scdf_init(zend_optimizer_ctx *ctx, scdf_ctx *scdf, zend_op_array *op_array, zend_ssa *ssa);
|
||||
void scdf_solve(scdf_ctx *scdf, const char *name);
|
||||
|
||||
int scdf_remove_unreachable_blocks(scdf_ctx *scdf);
|
||||
|
||||
/* Add uses to worklist */
|
||||
static inline void scdf_add_to_worklist(scdf_ctx *scdf, int var_num) {
|
||||
zend_ssa *ssa = scdf->ssa;
|
||||
zend_ssa_var *var = &ssa->vars[var_num];
|
||||
int use;
|
||||
zend_ssa_phi *phi;
|
||||
FOREACH_USE(var, use) {
|
||||
zend_bitset_incl(scdf->instr_worklist, use);
|
||||
} FOREACH_USE_END();
|
||||
FOREACH_PHI_USE(var, phi) {
|
||||
zend_bitset_incl(scdf->phi_var_worklist, phi->ssa_var);
|
||||
} FOREACH_PHI_USE_END();
|
||||
}
|
||||
|
||||
/* This should usually not be necessary, however it's used for type narrowing. */
|
||||
static inline void scdf_add_def_to_worklist(scdf_ctx *scdf, int var_num) {
|
||||
zend_ssa_var *var = &scdf->ssa->vars[var_num];
|
||||
if (var->definition >= 0) {
|
||||
zend_bitset_incl(scdf->instr_worklist, var->definition);
|
||||
} else if (var->definition_phi) {
|
||||
zend_bitset_incl(scdf->phi_var_worklist, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t scdf_edge(zend_cfg *cfg, int from, int to) {
|
||||
zend_basic_block *to_block = cfg->blocks + to;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < to_block->predecessors_count; i++) {
|
||||
uint32_t edge = to_block->predecessor_offset + i;
|
||||
|
||||
if (cfg->predecessors[edge] == from) {
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
ZEND_UNREACHABLE();
|
||||
}
|
||||
|
||||
static inline bool scdf_is_edge_feasible(scdf_ctx *scdf, int from, int to) {
|
||||
uint32_t edge = scdf_edge(&scdf->ssa->cfg, from, to);
|
||||
return zend_bitset_in(scdf->feasible_edges, edge);
|
||||
}
|
||||
|
||||
void scdf_mark_edge_feasible(scdf_ctx *scdf, int from, int to);
|
||||
|
||||
#endif
|
||||
@@ -1,378 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, SSA validation |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Nikita Popov <nikic@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "Optimizer/zend_optimizer_internal.h"
|
||||
|
||||
/* The ssa_verify_integrity() function ensures that that certain invariants of the SSA form and
|
||||
* CFG are upheld and prints messages to stderr if this is not the case. */
|
||||
|
||||
static inline bool is_in_use_chain(zend_ssa *ssa, int var, int check) {
|
||||
int use;
|
||||
FOREACH_USE(&ssa->vars[var], use) {
|
||||
if (use == check) {
|
||||
return 1;
|
||||
}
|
||||
} FOREACH_USE_END();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_in_phi_use_chain(zend_ssa *ssa, int var, zend_ssa_phi *check) {
|
||||
zend_ssa_phi *phi;
|
||||
FOREACH_PHI_USE(&ssa->vars[var], phi) {
|
||||
if (phi == check) {
|
||||
return 1;
|
||||
}
|
||||
} FOREACH_PHI_USE_END();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_used_by_op(zend_ssa *ssa, int op, int check) {
|
||||
zend_ssa_op *ssa_op = &ssa->ops[op];
|
||||
return (ssa_op->op1_use == check)
|
||||
|| (ssa_op->op2_use == check)
|
||||
|| (ssa_op->result_use == check);
|
||||
}
|
||||
|
||||
static inline bool is_defined_by_op(zend_ssa *ssa, int op, int check) {
|
||||
zend_ssa_op *ssa_op = &ssa->ops[op];
|
||||
return (ssa_op->op1_def == check)
|
||||
|| (ssa_op->op2_def == check)
|
||||
|| (ssa_op->result_def == check);
|
||||
}
|
||||
|
||||
static inline bool is_in_phi_sources(zend_ssa *ssa, zend_ssa_phi *phi, int check) {
|
||||
int source;
|
||||
FOREACH_PHI_SOURCE(phi, source) {
|
||||
if (source == check) {
|
||||
return 1;
|
||||
}
|
||||
} FOREACH_PHI_SOURCE_END();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_in_predecessors(zend_cfg *cfg, zend_basic_block *block, int check) {
|
||||
int i, *predecessors = &cfg->predecessors[block->predecessor_offset];
|
||||
for (i = 0; i < block->predecessors_count; i++) {
|
||||
if (predecessors[i] == check) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_in_successors(zend_basic_block *block, int check) {
|
||||
int s;
|
||||
for (s = 0; s < block->successors_count; s++) {
|
||||
if (block->successors[s] == check) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool is_var_type(zend_uchar type) {
|
||||
return (type & (IS_CV|IS_VAR|IS_TMP_VAR)) != 0;
|
||||
}
|
||||
|
||||
#define FAIL(...) do { \
|
||||
if (status == SUCCESS) { \
|
||||
fprintf(stderr, "\nIn function %s::%s (%s):\n", \
|
||||
op_array->scope ? ZSTR_VAL(op_array->scope->name) : "", \
|
||||
op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}", extra); \
|
||||
} \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
status = FAILURE; \
|
||||
} while (0)
|
||||
|
||||
#define VARFMT "%d (%s%s)"
|
||||
#define VAR(i) \
|
||||
(i), (ssa->vars[i].var < op_array->last_var ? "CV $" : "TMP"), \
|
||||
(ssa->vars[i].var < op_array->last_var ? ZSTR_VAL(op_array->vars[ssa->vars[i].var]) : "")
|
||||
|
||||
#define INSTRFMT "%d (%s)"
|
||||
#define INSTR(i) \
|
||||
(i), (zend_get_opcode_name(op_array->opcodes[i].opcode))
|
||||
|
||||
int ssa_verify_integrity(zend_op_array *op_array, zend_ssa *ssa, const char *extra) {
|
||||
zend_cfg *cfg = &ssa->cfg;
|
||||
zend_ssa_phi *phi;
|
||||
int i, status = SUCCESS;
|
||||
|
||||
/* Vars */
|
||||
for (i = 0; i < ssa->vars_count; i++) {
|
||||
zend_ssa_var *var = &ssa->vars[i];
|
||||
int use, c;
|
||||
uint32_t type = ssa->var_info[i].type;
|
||||
|
||||
if (var->definition < 0 && !var->definition_phi && i > op_array->last_var) {
|
||||
if (var->use_chain >= 0) {
|
||||
FAIL("var " VARFMT " without def has op uses\n", VAR(i));
|
||||
}
|
||||
if (var->phi_use_chain) {
|
||||
FAIL("var " VARFMT " without def has phi uses\n", VAR(i));
|
||||
}
|
||||
}
|
||||
if (var->definition >= 0 && var->definition_phi) {
|
||||
FAIL("var " VARFMT " has both def and def_phi\n", VAR(i));
|
||||
}
|
||||
if (var->definition >= 0) {
|
||||
if (!is_defined_by_op(ssa, var->definition, i)) {
|
||||
FAIL("var " VARFMT " not defined by op " INSTRFMT "\n",
|
||||
VAR(i), INSTR(var->definition));
|
||||
}
|
||||
}
|
||||
if (var->definition_phi) {
|
||||
if (var->definition_phi->ssa_var != i) {
|
||||
FAIL("var " VARFMT " not defined by given phi\n", VAR(i));
|
||||
}
|
||||
}
|
||||
|
||||
c = 0;
|
||||
FOREACH_USE(var, use) {
|
||||
if (++c > 10000) {
|
||||
FAIL("cycle in uses of " VARFMT "\n", VAR(i));
|
||||
return status;
|
||||
}
|
||||
if (!is_used_by_op(ssa, use, i)) {
|
||||
fprintf(stderr, "var " VARFMT " not in uses of op %d\n", VAR(i), use);
|
||||
}
|
||||
} FOREACH_USE_END();
|
||||
|
||||
c = 0;
|
||||
FOREACH_PHI_USE(var, phi) {
|
||||
if (++c > 10000) {
|
||||
FAIL("cycle in phi uses of " VARFMT "\n", VAR(i));
|
||||
return status;
|
||||
}
|
||||
if (!is_in_phi_sources(ssa, phi, i)) {
|
||||
FAIL("var " VARFMT " not in phi sources of %d\n", VAR(i), phi->ssa_var);
|
||||
}
|
||||
} FOREACH_PHI_USE_END();
|
||||
|
||||
if ((type & MAY_BE_ARRAY_KEY_ANY) && !(type & MAY_BE_ARRAY_OF_ANY)) {
|
||||
FAIL("var " VARFMT " has array key type but not value type\n", VAR(i));
|
||||
}
|
||||
if ((type & MAY_BE_ARRAY_OF_ANY) && !(type & MAY_BE_ARRAY_KEY_ANY)) {
|
||||
FAIL("var " VARFMT " has array value type but not key type\n", VAR(i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Instructions */
|
||||
FOREACH_INSTR_NUM(i) {
|
||||
zend_ssa_op *ssa_op = &ssa->ops[i];
|
||||
zend_op *opline = &op_array->opcodes[i];
|
||||
if (is_var_type(opline->op1_type)) {
|
||||
if (ssa_op->op1_use < 0 && ssa_op->op1_def < 0) {
|
||||
FAIL("var op1 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
|
||||
}
|
||||
} else {
|
||||
if (ssa_op->op1_use >= 0 || ssa_op->op1_def >= 0) {
|
||||
FAIL("non-var op1 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
|
||||
}
|
||||
}
|
||||
if (is_var_type(opline->op2_type)) {
|
||||
if (ssa_op->op2_use < 0 && ssa_op->op2_def < 0) {
|
||||
FAIL("var op2 of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
|
||||
}
|
||||
} else {
|
||||
if (ssa_op->op2_use >= 0 || ssa_op->op2_def >= 0) {
|
||||
FAIL("non-var op2 of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
|
||||
}
|
||||
}
|
||||
if (is_var_type(opline->result_type)) {
|
||||
if (ssa_op->result_use < 0 && ssa_op->result_def < 0) {
|
||||
FAIL("var result of " INSTRFMT " does not use/def an ssa var\n", INSTR(i));
|
||||
}
|
||||
} else {
|
||||
if (ssa_op->result_use >= 0 || ssa_op->result_def >= 0) {
|
||||
FAIL("non-var result of " INSTRFMT " uses or defs an ssa var\n", INSTR(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (ssa_op->op1_use >= 0) {
|
||||
if (ssa_op->op1_use >= ssa->vars_count) {
|
||||
FAIL("op1 use %d out of range\n", ssa_op->op1_use);
|
||||
}
|
||||
if (!is_in_use_chain(ssa, ssa_op->op1_use, i)) {
|
||||
FAIL("op1 use of " VARFMT " in " INSTRFMT " not in use chain\n",
|
||||
VAR(ssa_op->op1_use), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_use].var) {
|
||||
FAIL("op1 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->op1_use), VAR_NUM(opline->op1.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->op2_use >= 0) {
|
||||
if (ssa_op->op2_use >= ssa->vars_count) {
|
||||
FAIL("op2 use %d out of range\n", ssa_op->op2_use);
|
||||
}
|
||||
if (!is_in_use_chain(ssa, ssa_op->op2_use, i)) {
|
||||
FAIL("op2 use of " VARFMT " in " INSTRFMT " not in use chain\n",
|
||||
VAR(ssa_op->op2_use), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_use].var) {
|
||||
FAIL("op2 use of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->op2_use), VAR_NUM(opline->op2.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->result_use >= 0) {
|
||||
if (ssa_op->result_use >= ssa->vars_count) {
|
||||
FAIL("result use %d out of range\n", ssa_op->result_use);
|
||||
}
|
||||
if (!is_in_use_chain(ssa, ssa_op->result_use, i)) {
|
||||
FAIL("result use of " VARFMT " in " INSTRFMT " not in use chain\n",
|
||||
VAR(ssa_op->result_use), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_use].var) {
|
||||
FAIL("result use of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->result_use), VAR_NUM(opline->result.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->op1_def >= 0) {
|
||||
if (ssa_op->op1_def >= ssa->vars_count) {
|
||||
FAIL("op1 def %d out of range\n", ssa_op->op1_def);
|
||||
}
|
||||
if (ssa->vars[ssa_op->op1_def].definition != i) {
|
||||
FAIL("op1 def of " VARFMT " in " INSTRFMT " invalid\n",
|
||||
VAR(ssa_op->op1_def), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->op1.var) != ssa->vars[ssa_op->op1_def].var) {
|
||||
FAIL("op1 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->op1_def), VAR_NUM(opline->op1.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->op2_def >= 0) {
|
||||
if (ssa_op->op2_def >= ssa->vars_count) {
|
||||
FAIL("op2 def %d out of range\n", ssa_op->op2_def);
|
||||
}
|
||||
if (ssa->vars[ssa_op->op2_def].definition != i) {
|
||||
FAIL("op2 def of " VARFMT " in " INSTRFMT " invalid\n",
|
||||
VAR(ssa_op->op2_def), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->op2.var) != ssa->vars[ssa_op->op2_def].var) {
|
||||
FAIL("op2 def of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->op2_def), VAR_NUM(opline->op2.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
if (ssa_op->result_def >= 0) {
|
||||
if (ssa_op->result_def >= ssa->vars_count) {
|
||||
FAIL("result def %d out of range\n", ssa_op->result_def);
|
||||
}
|
||||
if (ssa->vars[ssa_op->result_def].definition != i) {
|
||||
FAIL("result def of " VARFMT " in " INSTRFMT " invalid\n",
|
||||
VAR(ssa_op->result_def), INSTR(i));
|
||||
}
|
||||
if (VAR_NUM(opline->result.var) != ssa->vars[ssa_op->result_def].var) {
|
||||
FAIL("result def of " VARFMT " does not match op %d of " INSTRFMT "\n",
|
||||
VAR(ssa_op->result_def), VAR_NUM(opline->result.var), INSTR(i));
|
||||
}
|
||||
}
|
||||
} FOREACH_INSTR_NUM_END();
|
||||
|
||||
/* Phis */
|
||||
FOREACH_PHI(phi) {
|
||||
unsigned num_sources = NUM_PHI_SOURCES(phi);
|
||||
for (i = 0; i < num_sources; i++) {
|
||||
int source = phi->sources[i];
|
||||
if (source < 0) {
|
||||
FAIL(VARFMT " negative source\n", VAR(phi->ssa_var));
|
||||
}
|
||||
if (!is_in_phi_use_chain(ssa, source, phi)) {
|
||||
FAIL(VARFMT " not in phi use chain of %d\n", VAR(phi->ssa_var), source);
|
||||
}
|
||||
if (ssa->vars[source].var != ssa->vars[phi->ssa_var].var) {
|
||||
FAIL(VARFMT " source of phi for " VARFMT "\n", VAR(source), VAR(phi->ssa_var));
|
||||
}
|
||||
if (phi->use_chains[i]) {
|
||||
int j;
|
||||
for (j = i + 1; j < num_sources; j++) {
|
||||
if (phi->sources[j] == source && phi->use_chains[j]) {
|
||||
FAIL("use chain for source " VARFMT " of phi " VARFMT
|
||||
" at %d despite earlier use\n", VAR(source), VAR(phi->ssa_var), j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ssa->vars[phi->ssa_var].definition_phi != phi) {
|
||||
FAIL(VARFMT " does not define this phi\n", VAR(phi->ssa_var));
|
||||
}
|
||||
} FOREACH_PHI_END();
|
||||
|
||||
/* Blocks */
|
||||
for (i = 0; i < cfg->blocks_count; i++) {
|
||||
zend_basic_block *block = &cfg->blocks[i];
|
||||
int *predecessors = &cfg->predecessors[block->predecessor_offset];
|
||||
int s, j;
|
||||
|
||||
if (i != 0 && block->start < (block-1)->start + (block-1)->len) {
|
||||
FAIL("Block %d start %d smaller previous end %d\n",
|
||||
i, block->start, (block-1)->start + (block-1)->len);
|
||||
}
|
||||
if (i != cfg->blocks_count-1 && block->start + block->len > (block+1)->start) {
|
||||
FAIL("Block %d end %d greater next start %d\n",
|
||||
i, block->start + block->len, (block+1)->start);
|
||||
}
|
||||
|
||||
for (j = block->start; j < block->start + block->len; j++) {
|
||||
if (cfg->map[j] != i) {
|
||||
FAIL("Instr " INSTRFMT " not associated with block %d\n", INSTR(j), i);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(block->flags & ZEND_BB_REACHABLE)) {
|
||||
if (ssa->blocks[i].phis) {
|
||||
FAIL("Unreachable block %d has phis\n", i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (s = 0; s < block->successors_count; s++) {
|
||||
zend_basic_block *next_block;
|
||||
if (block->successors[s] < 0) {
|
||||
FAIL("Successor number %d of %d negative", s, i);
|
||||
}
|
||||
next_block = &cfg->blocks[block->successors[s]];
|
||||
if (!(next_block->flags & ZEND_BB_REACHABLE)) {
|
||||
FAIL("Successor %d of %d not reachable\n", block->successors[s], i);
|
||||
}
|
||||
if (!is_in_predecessors(cfg, next_block, i)) {
|
||||
FAIL("Block %d predecessors missing %d\n", block->successors[s], i);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < block->predecessors_count; j++) {
|
||||
if (predecessors[j] >= 0) {
|
||||
int k;
|
||||
zend_basic_block *prev_block = &cfg->blocks[predecessors[j]];
|
||||
if (!(prev_block->flags & ZEND_BB_REACHABLE)) {
|
||||
FAIL("Predecessor %d of %d not reachable\n", predecessors[j], i);
|
||||
}
|
||||
if (!is_in_successors(prev_block, i)) {
|
||||
FAIL("Block %d successors missing %d\n", predecessors[j], i);
|
||||
}
|
||||
for (k = 0; k < block->predecessors_count; k++) {
|
||||
if (k != j && predecessors[k] == predecessors[j]) {
|
||||
FAIL("Block %d has duplicate predecessor %d\n", i, predecessors[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Call Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "zend_compile.h"
|
||||
#include "zend_extensions.h"
|
||||
#include "Optimizer/zend_optimizer.h"
|
||||
#include "zend_optimizer_internal.h"
|
||||
#include "zend_inference.h"
|
||||
#include "zend_call_graph.h"
|
||||
#include "zend_func_info.h"
|
||||
#include "zend_inference.h"
|
||||
#include "zend_call_graph.h"
|
||||
|
||||
static void zend_op_array_calc(zend_op_array *op_array, void *context)
|
||||
{
|
||||
zend_call_graph *call_graph = context;
|
||||
call_graph->op_arrays_count++;
|
||||
}
|
||||
|
||||
static void zend_op_array_collect(zend_op_array *op_array, void *context)
|
||||
{
|
||||
zend_call_graph *call_graph = context;
|
||||
zend_func_info *func_info = call_graph->func_infos + call_graph->op_arrays_count;
|
||||
|
||||
ZEND_SET_FUNC_INFO(op_array, func_info);
|
||||
call_graph->op_arrays[call_graph->op_arrays_count] = op_array;
|
||||
func_info->num = call_graph->op_arrays_count;
|
||||
call_graph->op_arrays_count++;
|
||||
}
|
||||
|
||||
ZEND_API int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info)
|
||||
{
|
||||
zend_op *opline = op_array->opcodes;
|
||||
zend_op *end = opline + op_array->last;
|
||||
zend_function *func;
|
||||
zend_call_info *call_info;
|
||||
int call = 0;
|
||||
zend_call_info **call_stack;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
bool is_prototype;
|
||||
|
||||
call_stack = do_alloca((op_array->last / 2) * sizeof(zend_call_info*), use_heap);
|
||||
call_info = NULL;
|
||||
while (opline != end) {
|
||||
switch (opline->opcode) {
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_METHOD_CALL:
|
||||
case ZEND_INIT_STATIC_METHOD_CALL:
|
||||
call_stack[call] = call_info;
|
||||
func = zend_optimizer_get_called_func(
|
||||
script, op_array, opline, &is_prototype);
|
||||
if (func) {
|
||||
call_info = zend_arena_calloc(arena, 1, sizeof(zend_call_info) + (sizeof(zend_send_arg_info) * ((int)opline->extended_value - 1)));
|
||||
call_info->caller_op_array = op_array;
|
||||
call_info->caller_init_opline = opline;
|
||||
call_info->caller_call_opline = NULL;
|
||||
call_info->callee_func = func;
|
||||
call_info->num_args = opline->extended_value;
|
||||
call_info->next_callee = func_info->callee_info;
|
||||
call_info->is_prototype = is_prototype;
|
||||
func_info->callee_info = call_info;
|
||||
|
||||
if (build_flags & ZEND_CALL_TREE) {
|
||||
call_info->next_caller = NULL;
|
||||
} else if (func->type == ZEND_INTERNAL_FUNCTION) {
|
||||
call_info->next_caller = NULL;
|
||||
} else {
|
||||
zend_func_info *callee_func_info = ZEND_FUNC_INFO(&func->op_array);
|
||||
if (callee_func_info) {
|
||||
call_info->next_caller = callee_func_info->caller_info;
|
||||
callee_func_info->caller_info = call_info;
|
||||
} else {
|
||||
call_info->next_caller = NULL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
call_info = NULL;
|
||||
}
|
||||
call++;
|
||||
break;
|
||||
case ZEND_INIT_FCALL_BY_NAME:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
case ZEND_INIT_DYNAMIC_CALL:
|
||||
case ZEND_NEW:
|
||||
case ZEND_INIT_USER_CALL:
|
||||
call_stack[call] = call_info;
|
||||
call_info = NULL;
|
||||
call++;
|
||||
break;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_ICALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
func_info->flags |= ZEND_FUNC_HAS_CALLS;
|
||||
if (call_info) {
|
||||
call_info->caller_call_opline = opline;
|
||||
}
|
||||
call--;
|
||||
call_info = call_stack[call];
|
||||
break;
|
||||
case ZEND_SEND_VAL:
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_SEND_VAL_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_USER:
|
||||
if (call_info) {
|
||||
if (opline->op2_type == IS_CONST) {
|
||||
call_info->named_args = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t num = opline->op2.num;
|
||||
if (num > 0) {
|
||||
num--;
|
||||
}
|
||||
call_info->arg_info[num].opline = opline;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_ARRAY:
|
||||
case ZEND_SEND_UNPACK:
|
||||
if (call_info) {
|
||||
call_info->send_unpack = 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_EXIT:
|
||||
/* In this case the DO_CALL opcode may have been dropped
|
||||
* and caller_call_opline will be NULL. */
|
||||
break;
|
||||
}
|
||||
opline++;
|
||||
}
|
||||
free_alloca(call_stack, use_heap);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static int zend_is_indirectly_recursive(zend_op_array *root, zend_op_array *op_array, zend_bitset visited)
|
||||
{
|
||||
zend_func_info *func_info;
|
||||
zend_call_info *call_info;
|
||||
int ret = 0;
|
||||
|
||||
if (op_array == root) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
func_info = ZEND_FUNC_INFO(op_array);
|
||||
if (zend_bitset_in(visited, func_info->num)) {
|
||||
return 0;
|
||||
}
|
||||
zend_bitset_incl(visited, func_info->num);
|
||||
call_info = func_info->caller_info;
|
||||
while (call_info) {
|
||||
if (zend_is_indirectly_recursive(root, call_info->caller_op_array, visited)) {
|
||||
call_info->recursive = 1;
|
||||
ret = 1;
|
||||
}
|
||||
call_info = call_info->next_caller;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void zend_analyze_recursion(zend_call_graph *call_graph)
|
||||
{
|
||||
zend_op_array *op_array;
|
||||
zend_func_info *func_info;
|
||||
zend_call_info *call_info;
|
||||
int i;
|
||||
int set_len = zend_bitset_len(call_graph->op_arrays_count);
|
||||
zend_bitset visited;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
|
||||
visited = ZEND_BITSET_ALLOCA(set_len, use_heap);
|
||||
for (i = 0; i < call_graph->op_arrays_count; i++) {
|
||||
op_array = call_graph->op_arrays[i];
|
||||
func_info = call_graph->func_infos + i;
|
||||
call_info = func_info->caller_info;
|
||||
for (; call_info; call_info = call_info->next_caller) {
|
||||
if (call_info->is_prototype) {
|
||||
/* Might be calling an overridden child method and not actually recursive. */
|
||||
continue;
|
||||
}
|
||||
if (call_info->caller_op_array == op_array) {
|
||||
call_info->recursive = 1;
|
||||
func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_DIRECTLY;
|
||||
} else {
|
||||
memset(visited, 0, sizeof(zend_ulong) * set_len);
|
||||
if (zend_is_indirectly_recursive(op_array, call_info->caller_op_array, visited)) {
|
||||
call_info->recursive = 1;
|
||||
func_info->flags |= ZEND_FUNC_RECURSIVE | ZEND_FUNC_RECURSIVE_INDIRECTLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(visited, use_heap);
|
||||
}
|
||||
|
||||
static void zend_sort_op_arrays(zend_call_graph *call_graph)
|
||||
{
|
||||
(void) call_graph;
|
||||
|
||||
// TODO: perform topological sort of cyclic call graph
|
||||
}
|
||||
|
||||
ZEND_API int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */
|
||||
{
|
||||
call_graph->op_arrays_count = 0;
|
||||
zend_foreach_op_array(script, zend_op_array_calc, call_graph);
|
||||
|
||||
call_graph->op_arrays = (zend_op_array**)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_op_array*));
|
||||
call_graph->func_infos = (zend_func_info*)zend_arena_calloc(arena, call_graph->op_arrays_count, sizeof(zend_func_info));
|
||||
call_graph->op_arrays_count = 0;
|
||||
zend_foreach_op_array(script, zend_op_array_collect, call_graph);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph) /* {{{ */
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < call_graph->op_arrays_count; i++) {
|
||||
zend_analyze_calls(arena, script, 0, call_graph->op_arrays[i], call_graph->func_infos + i);
|
||||
}
|
||||
zend_analyze_recursion(call_graph);
|
||||
zend_sort_op_arrays(call_graph);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array) /* {{{ */
|
||||
{
|
||||
zend_call_info **map, *call;
|
||||
if (!info->callee_info) {
|
||||
/* Don't build call map if function contains no calls */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
map = zend_arena_calloc(arena, sizeof(zend_call_info *), op_array->last);
|
||||
for (call = info->callee_info; call; call = call->next_callee) {
|
||||
int i;
|
||||
map[call->caller_init_opline - op_array->opcodes] = call;
|
||||
if (call->caller_call_opline) {
|
||||
map[call->caller_call_opline - op_array->opcodes] = call;
|
||||
}
|
||||
for (i = 0; i < call->num_args; i++) {
|
||||
if (call->arg_info[i].opline) {
|
||||
map[call->arg_info[i].opline - op_array->opcodes] = call;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Call Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_CALL_GRAPH_H
|
||||
#define ZEND_CALL_GRAPH_H
|
||||
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_func_info.h"
|
||||
#include "zend_optimizer.h"
|
||||
|
||||
typedef struct _zend_send_arg_info {
|
||||
zend_op *opline;
|
||||
} zend_send_arg_info;
|
||||
|
||||
struct _zend_call_info {
|
||||
zend_op_array *caller_op_array;
|
||||
zend_op *caller_init_opline;
|
||||
zend_op *caller_call_opline;
|
||||
zend_function *callee_func;
|
||||
zend_call_info *next_caller;
|
||||
zend_call_info *next_callee;
|
||||
bool recursive;
|
||||
bool send_unpack; /* Parameters passed by SEND_UNPACK or SEND_ARRAY */
|
||||
bool named_args; /* Function has named arguments */
|
||||
bool is_prototype; /* An overridden child method may be called */
|
||||
int num_args;
|
||||
zend_send_arg_info arg_info[1];
|
||||
};
|
||||
|
||||
struct _zend_func_info {
|
||||
int num;
|
||||
uint32_t flags;
|
||||
zend_ssa ssa; /* Static Single Assignment Form */
|
||||
zend_call_info *caller_info; /* where this function is called from */
|
||||
zend_call_info *callee_info; /* which functions are called from this one */
|
||||
zend_call_info **call_map; /* Call info associated with init/call/send opnum */
|
||||
zend_ssa_var_info return_info;
|
||||
};
|
||||
|
||||
typedef struct _zend_call_graph {
|
||||
int op_arrays_count;
|
||||
zend_op_array **op_arrays;
|
||||
zend_func_info *func_infos;
|
||||
} zend_call_graph;
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API int zend_build_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph);
|
||||
ZEND_API void zend_analyze_call_graph(zend_arena **arena, zend_script *script, zend_call_graph *call_graph);
|
||||
ZEND_API zend_call_info **zend_build_call_map(zend_arena **arena, zend_func_info *info, const zend_op_array *op_array);
|
||||
ZEND_API int zend_analyze_calls(zend_arena **arena, zend_script *script, uint32_t build_flags, zend_op_array *op_array, zend_func_info *func_info);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_CALL_GRAPH_H */
|
||||
@@ -1,912 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, CFG - Control Flow Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "zend_compile.h"
|
||||
#include "zend_cfg.h"
|
||||
#include "zend_func_info.h"
|
||||
#include "zend_worklist.h"
|
||||
#include "zend_optimizer.h"
|
||||
#include "zend_optimizer_internal.h"
|
||||
#include "zend_sort.h"
|
||||
|
||||
static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
|
||||
{
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
|
||||
while (1) {
|
||||
int i;
|
||||
|
||||
b->flags |= ZEND_BB_REACHABLE;
|
||||
if (b->successors_count == 0) {
|
||||
b->flags |= ZEND_BB_EXIT;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < b->successors_count; i++) {
|
||||
zend_basic_block *succ = blocks + b->successors[i];
|
||||
|
||||
if (b->len != 0) {
|
||||
zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
|
||||
if (b->successors_count == 1) {
|
||||
if (opcode == ZEND_JMP) {
|
||||
succ->flags |= ZEND_BB_TARGET;
|
||||
} else {
|
||||
succ->flags |= ZEND_BB_FOLLOW;
|
||||
|
||||
if ((cfg->flags & ZEND_CFG_STACKLESS)) {
|
||||
if (opcode == ZEND_INCLUDE_OR_EVAL ||
|
||||
opcode == ZEND_GENERATOR_CREATE ||
|
||||
opcode == ZEND_YIELD ||
|
||||
opcode == ZEND_YIELD_FROM ||
|
||||
opcode == ZEND_DO_FCALL ||
|
||||
opcode == ZEND_DO_UCALL ||
|
||||
opcode == ZEND_DO_FCALL_BY_NAME) {
|
||||
succ->flags |= ZEND_BB_ENTRY;
|
||||
}
|
||||
}
|
||||
if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) {
|
||||
if (opcode == ZEND_RECV ||
|
||||
opcode == ZEND_RECV_INIT) {
|
||||
succ->flags |= ZEND_BB_RECV_ENTRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (b->successors_count == 2) {
|
||||
if (i == 0 || opcode == ZEND_JMPZNZ) {
|
||||
succ->flags |= ZEND_BB_TARGET;
|
||||
} else {
|
||||
succ->flags |= ZEND_BB_FOLLOW;
|
||||
}
|
||||
} else {
|
||||
ZEND_ASSERT(
|
||||
opcode == ZEND_SWITCH_LONG
|
||||
|| opcode == ZEND_SWITCH_STRING
|
||||
|| opcode == ZEND_MATCH
|
||||
);
|
||||
if (i == b->successors_count - 1) {
|
||||
succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
|
||||
} else {
|
||||
succ->flags |= ZEND_BB_TARGET;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
succ->flags |= ZEND_BB_FOLLOW;
|
||||
}
|
||||
|
||||
if (i == b->successors_count - 1) {
|
||||
/* Tail call optimization */
|
||||
if (succ->flags & ZEND_BB_REACHABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
b = succ;
|
||||
break;
|
||||
} else {
|
||||
/* Recursively check reachability */
|
||||
if (!(succ->flags & ZEND_BB_REACHABLE)) {
|
||||
zend_mark_reachable(opcodes, cfg, succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
|
||||
{
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
|
||||
blocks[start].flags = ZEND_BB_START;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
|
||||
|
||||
if (op_array->last_try_catch) {
|
||||
zend_basic_block *b;
|
||||
int j, changed;
|
||||
uint32_t *block_map = cfg->map;
|
||||
|
||||
do {
|
||||
changed = 0;
|
||||
|
||||
/* Add exception paths */
|
||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
||||
|
||||
/* check for jumps into the middle of try block */
|
||||
b = blocks + block_map[op_array->try_catch_array[j].try_op];
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
zend_basic_block *end;
|
||||
|
||||
if (op_array->try_catch_array[j].catch_op) {
|
||||
end = blocks + block_map[op_array->try_catch_array[j].catch_op];
|
||||
while (b != end) {
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
op_array->try_catch_array[j].try_op = b->start;
|
||||
break;
|
||||
}
|
||||
b++;
|
||||
}
|
||||
}
|
||||
b = blocks + block_map[op_array->try_catch_array[j].try_op];
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
if (op_array->try_catch_array[j].finally_op) {
|
||||
end = blocks + block_map[op_array->try_catch_array[j].finally_op];
|
||||
while (b != end) {
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
|
||||
break;
|
||||
}
|
||||
b++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b = blocks + block_map[op_array->try_catch_array[j].try_op];
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
b->flags |= ZEND_BB_TRY;
|
||||
if (op_array->try_catch_array[j].catch_op) {
|
||||
b = blocks + block_map[op_array->try_catch_array[j].catch_op];
|
||||
b->flags |= ZEND_BB_CATCH;
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, b);
|
||||
}
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_op) {
|
||||
b = blocks + block_map[op_array->try_catch_array[j].finally_op];
|
||||
b->flags |= ZEND_BB_FINALLY;
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, b);
|
||||
}
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_end) {
|
||||
b = blocks + block_map[op_array->try_catch_array[j].finally_end];
|
||||
b->flags |= ZEND_BB_FINALLY_END;
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
changed = 1;
|
||||
zend_mark_reachable(op_array->opcodes, cfg, b);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (op_array->try_catch_array[j].catch_op) {
|
||||
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_op) {
|
||||
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_end) {
|
||||
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
}
|
||||
|
||||
if (cfg->flags & ZEND_FUNC_FREE_LOOP_VAR) {
|
||||
zend_basic_block *b;
|
||||
int j;
|
||||
uint32_t *block_map = cfg->map;
|
||||
|
||||
/* Mark blocks that are unreachable, but free a loop var created in a reachable block. */
|
||||
for (b = blocks; b < blocks + cfg->blocks_count; b++) {
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = b->start; j < b->start + b->len; j++) {
|
||||
zend_op *opline = &op_array->opcodes[j];
|
||||
if (zend_optimizer_is_loop_var_free(opline)) {
|
||||
zend_op *def_opline = zend_optimizer_get_loop_var_def(op_array, opline);
|
||||
if (def_opline) {
|
||||
uint32_t def_block = block_map[def_opline - op_array->opcodes];
|
||||
if (blocks[def_block].flags & ZEND_BB_REACHABLE) {
|
||||
b->flags |= ZEND_BB_UNREACHABLE_FREE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
int i;
|
||||
int start = 0;
|
||||
|
||||
for (i = 0; i < cfg->blocks_count; i++) {
|
||||
if (blocks[i].flags & ZEND_BB_REACHABLE) {
|
||||
start = i;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* clear all flags */
|
||||
for (i = 0; i < cfg->blocks_count; i++) {
|
||||
blocks[i].flags = 0;
|
||||
}
|
||||
|
||||
zend_mark_reachable_blocks(op_array, cfg, start);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static void initialize_block(zend_basic_block *block) {
|
||||
block->flags = 0;
|
||||
block->successors = block->successors_storage;
|
||||
block->successors_count = 0;
|
||||
block->predecessors_count = 0;
|
||||
block->predecessor_offset = -1;
|
||||
block->idom = -1;
|
||||
block->loop_header = -1;
|
||||
block->level = -1;
|
||||
block->children = -1;
|
||||
block->next_child = -1;
|
||||
}
|
||||
|
||||
#define BB_START(i) do { \
|
||||
if (!block_map[i]) { blocks_count++;} \
|
||||
block_map[i]++; \
|
||||
} while (0)
|
||||
|
||||
ZEND_API int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
uint32_t i;
|
||||
int j;
|
||||
uint32_t *block_map;
|
||||
zend_function *fn;
|
||||
int blocks_count = 0;
|
||||
zend_basic_block *blocks;
|
||||
zval *zv;
|
||||
bool extra_entry_block = 0;
|
||||
|
||||
cfg->flags = build_flags & (ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY);
|
||||
|
||||
cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
|
||||
|
||||
/* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
|
||||
BB_START(0);
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
zend_op *opline = op_array->opcodes + i;
|
||||
switch (opline->opcode) {
|
||||
case ZEND_RECV:
|
||||
case ZEND_RECV_INIT:
|
||||
if (build_flags & ZEND_CFG_RECV_ENTRY) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_RETURN:
|
||||
case ZEND_RETURN_BY_REF:
|
||||
case ZEND_GENERATOR_RETURN:
|
||||
case ZEND_EXIT:
|
||||
case ZEND_MATCH_ERROR:
|
||||
case ZEND_VERIFY_NEVER_TYPE:
|
||||
if (i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_THROW:
|
||||
/* Don't treat THROW as terminator if it's used in expression context,
|
||||
* as we may lose live ranges when eliminating unreachable code. */
|
||||
if (opline->extended_value != ZEND_THROW_IS_EXPR && i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_INCLUDE_OR_EVAL:
|
||||
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_GENERATOR_CREATE:
|
||||
case ZEND_YIELD:
|
||||
case ZEND_YIELD_FROM:
|
||||
if (build_flags & ZEND_CFG_STACKLESS) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_UCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
flags |= ZEND_FUNC_HAS_CALLS;
|
||||
if (build_flags & ZEND_CFG_STACKLESS) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_DO_ICALL:
|
||||
flags |= ZEND_FUNC_HAS_CALLS;
|
||||
break;
|
||||
case ZEND_INIT_FCALL:
|
||||
case ZEND_INIT_NS_FCALL_BY_NAME:
|
||||
zv = CRT_CONSTANT(opline->op2);
|
||||
if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
|
||||
/* The third literal is the lowercased unqualified name */
|
||||
zv += 2;
|
||||
}
|
||||
if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
|
||||
if (fn->type == ZEND_INTERNAL_FUNCTION) {
|
||||
flags |= zend_optimizer_classify_function(
|
||||
Z_STR_P(zv), opline->extended_value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_FAST_CALL:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_FAST_RET:
|
||||
if (i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_JMP:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
|
||||
if (i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_JMPZNZ:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
|
||||
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
|
||||
if (i + 1 < op_array->last) {
|
||||
BB_START(i + 1);
|
||||
}
|
||||
break;
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_JMP_NULL:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_CATCH:
|
||||
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
|
||||
}
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
case ZEND_SWITCH_LONG:
|
||||
case ZEND_SWITCH_STRING:
|
||||
case ZEND_MATCH:
|
||||
{
|
||||
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
|
||||
zval *zv;
|
||||
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
|
||||
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
|
||||
BB_START(i + 1);
|
||||
break;
|
||||
}
|
||||
case ZEND_FETCH_R:
|
||||
case ZEND_FETCH_W:
|
||||
case ZEND_FETCH_RW:
|
||||
case ZEND_FETCH_FUNC_ARG:
|
||||
case ZEND_FETCH_IS:
|
||||
case ZEND_FETCH_UNSET:
|
||||
case ZEND_UNSET_VAR:
|
||||
case ZEND_ISSET_ISEMPTY_VAR:
|
||||
if (opline->extended_value & ZEND_FETCH_LOCAL) {
|
||||
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
|
||||
} else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
|
||||
!op_array->function_name) {
|
||||
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
|
||||
}
|
||||
break;
|
||||
case ZEND_FUNC_GET_ARGS:
|
||||
flags |= ZEND_FUNC_VARARG;
|
||||
break;
|
||||
case ZEND_EXT_STMT:
|
||||
flags |= ZEND_FUNC_HAS_EXTENDED_STMT;
|
||||
break;
|
||||
case ZEND_EXT_FCALL_BEGIN:
|
||||
case ZEND_EXT_FCALL_END:
|
||||
flags |= ZEND_FUNC_HAS_EXTENDED_FCALL;
|
||||
break;
|
||||
case ZEND_FREE:
|
||||
if (opline->extended_value == ZEND_FREE_SWITCH) {
|
||||
flags |= ZEND_FUNC_FREE_LOOP_VAR;
|
||||
}
|
||||
break;
|
||||
case ZEND_FE_FREE:
|
||||
flags |= ZEND_FUNC_FREE_LOOP_VAR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the entry block has predecessors, we may need to split it */
|
||||
if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
|
||||
&& op_array->last > 0 && block_map[0] > 1) {
|
||||
extra_entry_block = 1;
|
||||
}
|
||||
|
||||
if (op_array->last_try_catch) {
|
||||
for (j = 0; j < op_array->last_try_catch; j++) {
|
||||
BB_START(op_array->try_catch_array[j].try_op);
|
||||
if (op_array->try_catch_array[j].catch_op) {
|
||||
BB_START(op_array->try_catch_array[j].catch_op);
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_op) {
|
||||
BB_START(op_array->try_catch_array[j].finally_op);
|
||||
}
|
||||
if (op_array->try_catch_array[j].finally_end) {
|
||||
BB_START(op_array->try_catch_array[j].finally_end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blocks_count += extra_entry_block;
|
||||
cfg->blocks_count = blocks_count;
|
||||
|
||||
/* Build CFG, Step 2: Build Array of Basic Blocks */
|
||||
cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
|
||||
|
||||
blocks_count = -1;
|
||||
|
||||
if (extra_entry_block) {
|
||||
initialize_block(&blocks[0]);
|
||||
blocks[0].start = 0;
|
||||
blocks[0].len = 0;
|
||||
blocks_count++;
|
||||
}
|
||||
|
||||
for (i = 0; i < op_array->last; i++) {
|
||||
if (block_map[i]) {
|
||||
if (blocks_count >= 0) {
|
||||
blocks[blocks_count].len = i - blocks[blocks_count].start;
|
||||
}
|
||||
blocks_count++;
|
||||
initialize_block(&blocks[blocks_count]);
|
||||
blocks[blocks_count].start = i;
|
||||
}
|
||||
block_map[i] = blocks_count;
|
||||
}
|
||||
|
||||
blocks[blocks_count].len = i - blocks[blocks_count].start;
|
||||
blocks_count++;
|
||||
|
||||
/* Build CFG, Step 3: Calculate successors */
|
||||
for (j = 0; j < blocks_count; j++) {
|
||||
zend_basic_block *block = &blocks[j];
|
||||
zend_op *opline;
|
||||
if (block->len == 0) {
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = j + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
opline = op_array->opcodes + block->start + block->len - 1;
|
||||
switch (opline->opcode) {
|
||||
case ZEND_FAST_RET:
|
||||
case ZEND_RETURN:
|
||||
case ZEND_RETURN_BY_REF:
|
||||
case ZEND_GENERATOR_RETURN:
|
||||
case ZEND_EXIT:
|
||||
case ZEND_THROW:
|
||||
case ZEND_MATCH_ERROR:
|
||||
case ZEND_VERIFY_NEVER_TYPE:
|
||||
break;
|
||||
case ZEND_JMP:
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
|
||||
break;
|
||||
case ZEND_JMPZNZ:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
|
||||
block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
|
||||
break;
|
||||
case ZEND_JMPZ:
|
||||
case ZEND_JMPNZ:
|
||||
case ZEND_JMPZ_EX:
|
||||
case ZEND_JMPNZ_EX:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_ASSERT_CHECK:
|
||||
case ZEND_JMP_NULL:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
|
||||
block->successors[1] = j + 1;
|
||||
break;
|
||||
case ZEND_CATCH:
|
||||
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
|
||||
block->successors[1] = j + 1;
|
||||
} else {
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = j + 1;
|
||||
}
|
||||
break;
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
|
||||
block->successors[1] = j + 1;
|
||||
break;
|
||||
case ZEND_FE_RESET_R:
|
||||
case ZEND_FE_RESET_RW:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
|
||||
block->successors[1] = j + 1;
|
||||
break;
|
||||
case ZEND_FAST_CALL:
|
||||
block->successors_count = 2;
|
||||
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
|
||||
block->successors[1] = j + 1;
|
||||
break;
|
||||
case ZEND_SWITCH_LONG:
|
||||
case ZEND_SWITCH_STRING:
|
||||
case ZEND_MATCH:
|
||||
{
|
||||
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
|
||||
zval *zv;
|
||||
uint32_t s = 0;
|
||||
|
||||
block->successors_count = (opline->opcode == ZEND_MATCH ? 1 : 2) + zend_hash_num_elements(jumptable);
|
||||
block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
|
||||
|
||||
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
|
||||
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
|
||||
if (opline->opcode != ZEND_MATCH) {
|
||||
block->successors[s++] = j + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
block->successors_count = 1;
|
||||
block->successors[0] = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Build CFG, Step 4, Mark Reachable Basic Blocks */
|
||||
cfg->flags |= flags;
|
||||
zend_mark_reachable_blocks(op_array, cfg, 0);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
int j, s, edges;
|
||||
zend_basic_block *b;
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
zend_basic_block *end = blocks + cfg->blocks_count;
|
||||
int *predecessors;
|
||||
|
||||
edges = 0;
|
||||
for (b = blocks; b < end; b++) {
|
||||
b->predecessors_count = 0;
|
||||
}
|
||||
for (b = blocks; b < end; b++) {
|
||||
if (!(b->flags & ZEND_BB_REACHABLE)) {
|
||||
b->successors_count = 0;
|
||||
b->predecessors_count = 0;
|
||||
} else {
|
||||
for (s = 0; s < b->successors_count; s++) {
|
||||
edges++;
|
||||
blocks[b->successors[s]].predecessors_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg->edges_count = edges;
|
||||
cfg->predecessors = predecessors = (int*)zend_arena_calloc(arena, sizeof(int), edges);
|
||||
|
||||
edges = 0;
|
||||
for (b = blocks; b < end; b++) {
|
||||
if (b->flags & ZEND_BB_REACHABLE) {
|
||||
b->predecessor_offset = edges;
|
||||
edges += b->predecessors_count;
|
||||
b->predecessors_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < cfg->blocks_count; j++) {
|
||||
if (blocks[j].flags & ZEND_BB_REACHABLE) {
|
||||
/* SWITCH_STRING/LONG may have few identical successors */
|
||||
for (s = 0; s < blocks[j].successors_count; s++) {
|
||||
int duplicate = 0;
|
||||
int p;
|
||||
|
||||
for (p = 0; p < s; p++) {
|
||||
if (blocks[j].successors[p] == blocks[j].successors[s]) {
|
||||
duplicate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!duplicate) {
|
||||
zend_basic_block *b = blocks + blocks[j].successors[s];
|
||||
|
||||
predecessors[b->predecessor_offset + b->predecessors_count] = j;
|
||||
b->predecessors_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* Computes a postorder numbering of the CFG */
|
||||
static void compute_postnum_recursive(
|
||||
int *postnum, int *cur, const zend_cfg *cfg, int block_num) /* {{{ */
|
||||
{
|
||||
int s;
|
||||
zend_basic_block *block = &cfg->blocks[block_num];
|
||||
if (postnum[block_num] != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
postnum[block_num] = -2; /* Marker for "currently visiting" */
|
||||
for (s = 0; s < block->successors_count; s++) {
|
||||
compute_postnum_recursive(postnum, cur, cfg, block->successors[s]);
|
||||
}
|
||||
postnum[block_num] = (*cur)++;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* Computes dominator tree using algorithm from "A Simple, Fast Dominance Algorithm" by
|
||||
* Cooper, Harvey and Kennedy. */
|
||||
ZEND_API int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
int blocks_count = cfg->blocks_count;
|
||||
int j, k, changed;
|
||||
|
||||
ALLOCA_FLAG(use_heap)
|
||||
int *postnum = do_alloca(sizeof(int) * cfg->blocks_count, use_heap);
|
||||
memset(postnum, -1, sizeof(int) * cfg->blocks_count);
|
||||
j = 0;
|
||||
compute_postnum_recursive(postnum, &j, cfg, 0);
|
||||
|
||||
/* FIXME: move declarations */
|
||||
blocks[0].idom = 0;
|
||||
do {
|
||||
changed = 0;
|
||||
/* Iterating in RPO here would converge faster */
|
||||
for (j = 1; j < blocks_count; j++) {
|
||||
int idom = -1;
|
||||
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
for (k = 0; k < blocks[j].predecessors_count; k++) {
|
||||
int pred = cfg->predecessors[blocks[j].predecessor_offset + k];
|
||||
|
||||
if (idom < 0) {
|
||||
if (blocks[pred].idom >= 0)
|
||||
idom = pred;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (blocks[pred].idom >= 0) {
|
||||
while (idom != pred) {
|
||||
while (postnum[pred] < postnum[idom]) pred = blocks[pred].idom;
|
||||
while (postnum[idom] < postnum[pred]) idom = blocks[idom].idom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (idom >= 0 && blocks[j].idom != idom) {
|
||||
blocks[j].idom = idom;
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
blocks[0].idom = -1;
|
||||
|
||||
for (j = 1; j < blocks_count; j++) {
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (blocks[j].idom >= 0) {
|
||||
/* Sort by block number to traverse children in pre-order */
|
||||
if (blocks[blocks[j].idom].children < 0 ||
|
||||
j < blocks[blocks[j].idom].children) {
|
||||
blocks[j].next_child = blocks[blocks[j].idom].children;
|
||||
blocks[blocks[j].idom].children = j;
|
||||
} else {
|
||||
int k = blocks[blocks[j].idom].children;
|
||||
while (blocks[k].next_child >=0 && j > blocks[k].next_child) {
|
||||
k = blocks[k].next_child;
|
||||
}
|
||||
blocks[j].next_child = blocks[k].next_child;
|
||||
blocks[k].next_child = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < blocks_count; j++) {
|
||||
int idom = blocks[j].idom, level = 0;
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
while (idom >= 0) {
|
||||
level++;
|
||||
if (blocks[idom].level >= 0) {
|
||||
level += blocks[idom].level;
|
||||
break;
|
||||
} else {
|
||||
idom = blocks[idom].idom;
|
||||
}
|
||||
}
|
||||
blocks[j].level = level;
|
||||
}
|
||||
|
||||
free_alloca(postnum, use_heap);
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int dominates(zend_basic_block *blocks, int a, int b) /* {{{ */
|
||||
{
|
||||
while (blocks[b].level > blocks[a].level) {
|
||||
b = blocks[b].idom;
|
||||
}
|
||||
return a == b;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
int level;
|
||||
} block_info;
|
||||
static int compare_block_level(const block_info *a, const block_info *b) {
|
||||
return b->level - a->level;
|
||||
}
|
||||
static void swap_blocks(block_info *a, block_info *b) {
|
||||
block_info tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
|
||||
ZEND_API int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
|
||||
{
|
||||
int i, j, k, n;
|
||||
int time;
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
int *entry_times, *exit_times;
|
||||
zend_worklist work;
|
||||
int flag = ZEND_FUNC_NO_LOOPS;
|
||||
block_info *sorted_blocks;
|
||||
ALLOCA_FLAG(list_use_heap)
|
||||
ALLOCA_FLAG(tree_use_heap)
|
||||
ALLOCA_FLAG(sorted_blocks_use_heap)
|
||||
|
||||
ZEND_WORKLIST_ALLOCA(&work, cfg->blocks_count, list_use_heap);
|
||||
|
||||
/* We don't materialize the DJ spanning tree explicitly, as we are only interested in ancestor
|
||||
* queries. These are implemented by checking entry/exit times of the DFS search. */
|
||||
entry_times = do_alloca(2 * sizeof(int) * cfg->blocks_count, tree_use_heap);
|
||||
exit_times = entry_times + cfg->blocks_count;
|
||||
memset(entry_times, -1, 2 * sizeof(int) * cfg->blocks_count);
|
||||
|
||||
zend_worklist_push(&work, 0);
|
||||
time = 0;
|
||||
while (zend_worklist_len(&work)) {
|
||||
next:
|
||||
i = zend_worklist_peek(&work);
|
||||
if (entry_times[i] == -1) {
|
||||
entry_times[i] = time++;
|
||||
}
|
||||
/* Visit blocks immediately dominated by i. */
|
||||
for (j = blocks[i].children; j >= 0; j = blocks[j].next_child) {
|
||||
if (zend_worklist_push(&work, j)) {
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
/* Visit join edges. */
|
||||
for (j = 0; j < blocks[i].successors_count; j++) {
|
||||
int succ = blocks[i].successors[j];
|
||||
if (blocks[succ].idom == i) {
|
||||
continue;
|
||||
} else if (zend_worklist_push(&work, succ)) {
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
exit_times[i] = time++;
|
||||
zend_worklist_pop(&work);
|
||||
}
|
||||
|
||||
/* Sort blocks by decreasing level, which is the order in which we want to process them */
|
||||
sorted_blocks = do_alloca(sizeof(block_info) * cfg->blocks_count, sorted_blocks_use_heap);
|
||||
for (i = 0; i < cfg->blocks_count; i++) {
|
||||
sorted_blocks[i].id = i;
|
||||
sorted_blocks[i].level = blocks[i].level;
|
||||
}
|
||||
zend_sort(sorted_blocks, cfg->blocks_count, sizeof(block_info),
|
||||
(compare_func_t) compare_block_level, (swap_func_t) swap_blocks);
|
||||
|
||||
/* Identify loops. See Sreedhar et al, "Identifying Loops Using DJ
|
||||
Graphs". */
|
||||
|
||||
for (n = 0; n < cfg->blocks_count; n++) {
|
||||
i = sorted_blocks[n].id;
|
||||
|
||||
zend_bitset_clear(work.visited, zend_bitset_len(cfg->blocks_count));
|
||||
for (j = 0; j < blocks[i].predecessors_count; j++) {
|
||||
int pred = cfg->predecessors[blocks[i].predecessor_offset + j];
|
||||
|
||||
/* A join edge is one for which the predecessor does not
|
||||
immediately dominate the successor. */
|
||||
if (blocks[i].idom == pred) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* In a loop back-edge (back-join edge), the successor dominates
|
||||
the predecessor. */
|
||||
if (dominates(blocks, i, pred)) {
|
||||
blocks[i].flags |= ZEND_BB_LOOP_HEADER;
|
||||
flag &= ~ZEND_FUNC_NO_LOOPS;
|
||||
zend_worklist_push(&work, pred);
|
||||
} else {
|
||||
/* Otherwise it's a cross-join edge. See if it's a branch
|
||||
to an ancestor on the DJ spanning tree. */
|
||||
if (entry_times[pred] > entry_times[i] && exit_times[pred] < exit_times[i]) {
|
||||
blocks[i].flags |= ZEND_BB_IRREDUCIBLE_LOOP;
|
||||
flag |= ZEND_FUNC_IRREDUCIBLE;
|
||||
flag &= ~ZEND_FUNC_NO_LOOPS;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (zend_worklist_len(&work)) {
|
||||
j = zend_worklist_pop(&work);
|
||||
while (blocks[j].loop_header >= 0) {
|
||||
j = blocks[j].loop_header;
|
||||
}
|
||||
if (j != i) {
|
||||
blocks[j].loop_header = i;
|
||||
for (k = 0; k < blocks[j].predecessors_count; k++) {
|
||||
zend_worklist_push(&work, cfg->predecessors[blocks[j].predecessor_offset + k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(sorted_blocks, sorted_blocks_use_heap);
|
||||
free_alloca(entry_times, tree_use_heap);
|
||||
ZEND_WORKLIST_FREE_ALLOCA(&work, list_use_heap);
|
||||
|
||||
cfg->flags |= flag;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, CFG - Control Flow Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_CFG_H
|
||||
#define ZEND_CFG_H
|
||||
|
||||
/* zend_basic_block.flags */
|
||||
#define ZEND_BB_START (1<<0) /* first block */
|
||||
#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
|
||||
#define ZEND_BB_TARGET (1<<2) /* jump target */
|
||||
#define ZEND_BB_EXIT (1<<3) /* without successors */
|
||||
#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
|
||||
#define ZEND_BB_TRY (1<<5) /* start of try block */
|
||||
#define ZEND_BB_CATCH (1<<6) /* start of catch block */
|
||||
#define ZEND_BB_FINALLY (1<<7) /* start of finally block */
|
||||
#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
|
||||
#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
|
||||
#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
|
||||
|
||||
#define ZEND_BB_LOOP_HEADER (1<<16)
|
||||
#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
|
||||
|
||||
#define ZEND_BB_REACHABLE (1U<<31)
|
||||
|
||||
#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_UNREACHABLE_FREE)
|
||||
|
||||
typedef struct _zend_basic_block {
|
||||
int *successors; /* successor block indices */
|
||||
uint32_t flags;
|
||||
uint32_t start; /* first opcode number */
|
||||
uint32_t len; /* number of opcodes */
|
||||
int successors_count; /* number of successors */
|
||||
int predecessors_count; /* number of predecessors */
|
||||
int predecessor_offset; /* offset of 1-st predecessor */
|
||||
int idom; /* immediate dominator block */
|
||||
int loop_header; /* closest loop header, or -1 */
|
||||
int level; /* steps away from the entry in the dom. tree */
|
||||
int children; /* list of dominated blocks */
|
||||
int next_child; /* next dominated block */
|
||||
int successors_storage[2]; /* up to 2 successor blocks */
|
||||
} zend_basic_block;
|
||||
|
||||
/*
|
||||
+------------+---+---+---+---+---+
|
||||
| |OP1|OP2|EXT| 0 | 1 |
|
||||
+------------+---+---+---+---+---+
|
||||
|JMP |ADR| | |OP1| - |
|
||||
|JMPZ | |ADR| |OP2|FOL|
|
||||
|JMPNZ | |ADR| |OP2|FOL|
|
||||
|JMPZNZ | |ADR|ADR|OP2|EXT|
|
||||
|JMPZ_EX | |ADR| |OP2|FOL|
|
||||
|JMPNZ_EX | |ADR| |OP2|FOL|
|
||||
|JMP_SET | |ADR| |OP2|FOL|
|
||||
|COALESCE | |ADR| |OP2|FOL|
|
||||
|ASSERT_CHK | |ADR| |OP2|FOL|
|
||||
|NEW | |ADR| |OP2|FOL|
|
||||
|DCL_ANON* |ADR| | |OP1|FOL|
|
||||
|FE_RESET_* | |ADR| |OP2|FOL|
|
||||
|FE_FETCH_* | | |ADR|EXT|FOL|
|
||||
|CATCH | | |ADR|EXT|FOL|
|
||||
|FAST_CALL |ADR| | |OP1|FOL|
|
||||
|FAST_RET | | | | - | - |
|
||||
|RETURN* | | | | - | - |
|
||||
|EXIT | | | | - | - |
|
||||
|THROW | | | | - | - |
|
||||
|* | | | |FOL| - |
|
||||
+------------+---+---+---+---+---+
|
||||
*/
|
||||
|
||||
typedef struct _zend_cfg {
|
||||
int blocks_count; /* number of basic blocks */
|
||||
int edges_count; /* number of edges */
|
||||
zend_basic_block *blocks; /* array of basic blocks */
|
||||
int *predecessors;
|
||||
uint32_t *map;
|
||||
uint32_t flags;
|
||||
} zend_cfg;
|
||||
|
||||
/* Build Flags */
|
||||
#define ZEND_CFG_STACKLESS (1<<30)
|
||||
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
|
||||
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
|
||||
#define ZEND_SSA_RC_INFERENCE (1<<27)
|
||||
#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
|
||||
#define ZEND_CFG_RECV_ENTRY (1<<24)
|
||||
#define ZEND_CALL_TREE (1<<23)
|
||||
#define ZEND_SSA_USE_CV_RESULTS (1<<22)
|
||||
|
||||
#define CRT_CONSTANT_EX(op_array, opline, node) \
|
||||
(((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) ? \
|
||||
RT_CONSTANT(opline, (node)) \
|
||||
: \
|
||||
CT_CONSTANT_EX(op_array, (node).constant) \
|
||||
)
|
||||
|
||||
#define CRT_CONSTANT(node) \
|
||||
CRT_CONSTANT_EX(op_array, opline, node)
|
||||
|
||||
#define RETURN_VALUE_USED(opline) \
|
||||
((opline)->result_type != IS_UNUSED)
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
|
||||
void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg);
|
||||
ZEND_API int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg);
|
||||
ZEND_API int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg);
|
||||
ZEND_API int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_CFG_H */
|
||||
@@ -1,332 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, DFG - Data Flow Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "zend_compile.h"
|
||||
#include "zend_dfg.h"
|
||||
|
||||
static zend_always_inline void _zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
|
||||
{
|
||||
uint32_t var_num;
|
||||
const zend_op *next;
|
||||
|
||||
if (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
if (((opline->op2_type & (IS_VAR|IS_TMP_VAR)) != 0
|
||||
&& opline->opcode != ZEND_FE_FETCH_R
|
||||
&& opline->opcode != ZEND_FE_FETCH_RW)
|
||||
|| (opline->op2_type == IS_CV)) {
|
||||
var_num = EX_VAR_TO_NUM(opline->op2.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
if ((build_flags & ZEND_SSA_USE_CV_RESULTS)
|
||||
&& opline->result_type == IS_CV
|
||||
&& opline->opcode != ZEND_RECV) {
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
|
||||
switch (opline->opcode) {
|
||||
case ZEND_ASSIGN:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op2_type == IS_CV) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
add_op1_def:
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op1.var));
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_REF:
|
||||
if (opline->op2_type == IS_CV) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM:
|
||||
case ZEND_ASSIGN_OBJ:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if (build_flags & ZEND_SSA_RC_INFERENCE && next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OBJ_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if (next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_REF:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
if (next->op1_type == IS_CV) {
|
||||
zend_bitset_incl(def, var_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_STATIC_PROP_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_DIM_OP:
|
||||
case ZEND_ASSIGN_OBJ_OP:
|
||||
next = opline + 1;
|
||||
if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
var_num = EX_VAR_TO_NUM(next->op1.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
}
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ASSIGN_OP:
|
||||
case ZEND_PRE_INC:
|
||||
case ZEND_PRE_DEC:
|
||||
case ZEND_POST_INC:
|
||||
case ZEND_POST_DEC:
|
||||
case ZEND_BIND_GLOBAL:
|
||||
case ZEND_BIND_STATIC:
|
||||
case ZEND_SEND_VAR_NO_REF:
|
||||
case ZEND_SEND_VAR_NO_REF_EX:
|
||||
case ZEND_SEND_VAR_EX:
|
||||
case ZEND_SEND_FUNC_ARG:
|
||||
case ZEND_SEND_REF:
|
||||
case ZEND_SEND_UNPACK:
|
||||
case ZEND_FE_RESET_RW:
|
||||
case ZEND_MAKE_REF:
|
||||
case ZEND_PRE_INC_OBJ:
|
||||
case ZEND_PRE_DEC_OBJ:
|
||||
case ZEND_POST_INC_OBJ:
|
||||
case ZEND_POST_DEC_OBJ:
|
||||
case ZEND_UNSET_DIM:
|
||||
case ZEND_UNSET_OBJ:
|
||||
case ZEND_FETCH_DIM_W:
|
||||
case ZEND_FETCH_DIM_RW:
|
||||
case ZEND_FETCH_DIM_FUNC_ARG:
|
||||
case ZEND_FETCH_DIM_UNSET:
|
||||
case ZEND_FETCH_LIST_W:
|
||||
if (opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_SEND_VAR:
|
||||
case ZEND_CAST:
|
||||
case ZEND_QM_ASSIGN:
|
||||
case ZEND_JMP_SET:
|
||||
case ZEND_COALESCE:
|
||||
case ZEND_FE_RESET_R:
|
||||
if ((build_flags & ZEND_SSA_RC_INFERENCE) && opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_UNPACK:
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
break;
|
||||
case ZEND_ADD_ARRAY_ELEMENT:
|
||||
var_num = EX_VAR_TO_NUM(opline->result.var);
|
||||
if (!zend_bitset_in(def, var_num)) {
|
||||
zend_bitset_incl(use, var_num);
|
||||
}
|
||||
ZEND_FALLTHROUGH;
|
||||
case ZEND_INIT_ARRAY:
|
||||
if (((build_flags & ZEND_SSA_RC_INFERENCE)
|
||||
|| (opline->extended_value & ZEND_ARRAY_ELEMENT_REF))
|
||||
&& opline->op1_type == IS_CV) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_YIELD:
|
||||
if (opline->op1_type == IS_CV
|
||||
&& ((op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)
|
||||
|| (build_flags & ZEND_SSA_RC_INFERENCE))) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_UNSET_CV:
|
||||
goto add_op1_def;
|
||||
case ZEND_VERIFY_RETURN_TYPE:
|
||||
if (opline->op1_type & (IS_TMP_VAR|IS_VAR|IS_CV)) {
|
||||
goto add_op1_def;
|
||||
}
|
||||
break;
|
||||
case ZEND_FE_FETCH_R:
|
||||
case ZEND_FE_FETCH_RW:
|
||||
#if 0
|
||||
/* This special case was handled above the switch */
|
||||
if (opline->op2_type != IS_CV) {
|
||||
op2_use = -1; /* not used */
|
||||
}
|
||||
#endif
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
break;
|
||||
case ZEND_BIND_LEXICAL:
|
||||
if ((opline->extended_value & ZEND_BIND_REF) || (build_flags & ZEND_SSA_RC_INFERENCE)) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->op2.var));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (opline->result_type & (IS_CV|IS_VAR|IS_TMP_VAR)) {
|
||||
zend_bitset_incl(def, EX_VAR_TO_NUM(opline->result.var));
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
ZEND_API void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def) /* {{{ */
|
||||
{
|
||||
_zend_dfg_add_use_def_op(op_array, opline, build_flags, use, def);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags) /* {{{ */
|
||||
{
|
||||
int set_size;
|
||||
zend_basic_block *blocks = cfg->blocks;
|
||||
int blocks_count = cfg->blocks_count;
|
||||
zend_bitset tmp, def, use, in, out;
|
||||
int k;
|
||||
int j;
|
||||
|
||||
set_size = dfg->size;
|
||||
tmp = dfg->tmp;
|
||||
def = dfg->def;
|
||||
use = dfg->use;
|
||||
in = dfg->in;
|
||||
out = dfg->out;
|
||||
|
||||
/* Collect "def" and "use" sets */
|
||||
for (j = 0; j < blocks_count; j++) {
|
||||
zend_op *opline, *end;
|
||||
zend_bitset b_use, b_def;
|
||||
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
opline = op_array->opcodes + blocks[j].start;
|
||||
end = opline + blocks[j].len;
|
||||
b_use = DFG_BITSET(use, set_size, j);
|
||||
b_def = DFG_BITSET(def, set_size, j);
|
||||
for (; opline < end; opline++) {
|
||||
if (opline->opcode != ZEND_OP_DATA) {
|
||||
_zend_dfg_add_use_def_op(op_array, opline, build_flags, b_use, b_def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate "in" and "out" sets */
|
||||
{
|
||||
uint32_t worklist_len = zend_bitset_len(blocks_count);
|
||||
zend_bitset worklist;
|
||||
ALLOCA_FLAG(use_heap);
|
||||
worklist = ZEND_BITSET_ALLOCA(worklist_len, use_heap);
|
||||
memset(worklist, 0, worklist_len * ZEND_BITSET_ELM_SIZE);
|
||||
for (j = 0; j < blocks_count; j++) {
|
||||
zend_bitset_incl(worklist, j);
|
||||
}
|
||||
while (!zend_bitset_empty(worklist, worklist_len)) {
|
||||
/* We use the last block on the worklist, because predecessors tend to be located
|
||||
* before the succeeding block, so this converges faster. */
|
||||
j = zend_bitset_last(worklist, worklist_len);
|
||||
zend_bitset_excl(worklist, j);
|
||||
|
||||
if ((blocks[j].flags & ZEND_BB_REACHABLE) == 0) {
|
||||
continue;
|
||||
}
|
||||
if (blocks[j].successors_count != 0) {
|
||||
zend_bitset_copy(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[0]), set_size);
|
||||
for (k = 1; k < blocks[j].successors_count; k++) {
|
||||
zend_bitset_union(DFG_BITSET(out, set_size, j), DFG_BITSET(in, set_size, blocks[j].successors[k]), set_size);
|
||||
}
|
||||
} else {
|
||||
zend_bitset_clear(DFG_BITSET(out, set_size, j), set_size);
|
||||
}
|
||||
zend_bitset_union_with_difference(tmp, DFG_BITSET(use, set_size, j), DFG_BITSET(out, set_size, j), DFG_BITSET(def, set_size, j), set_size);
|
||||
if (!zend_bitset_equal(DFG_BITSET(in, set_size, j), tmp, set_size)) {
|
||||
zend_bitset_copy(DFG_BITSET(in, set_size, j), tmp, set_size);
|
||||
|
||||
/* Add predecessors of changed block to worklist */
|
||||
{
|
||||
int *predecessors = &cfg->predecessors[blocks[j].predecessor_offset];
|
||||
for (k = 0; k < blocks[j].predecessors_count; k++) {
|
||||
zend_bitset_incl(worklist, predecessors[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free_alloca(worklist, use_heap);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, DFG - Data Flow Graph |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_DFG_H
|
||||
#define ZEND_DFG_H
|
||||
|
||||
#include "zend_bitset.h"
|
||||
#include "zend_cfg.h"
|
||||
|
||||
typedef struct _zend_dfg {
|
||||
int vars;
|
||||
uint32_t size;
|
||||
zend_bitset tmp;
|
||||
zend_bitset def;
|
||||
zend_bitset use;
|
||||
zend_bitset in;
|
||||
zend_bitset out;
|
||||
} zend_dfg;
|
||||
|
||||
#define DFG_BITSET(set, set_size, block_num) \
|
||||
((set) + ((block_num) * (set_size)))
|
||||
|
||||
#define DFG_SET(set, set_size, block_num, var_num) \
|
||||
zend_bitset_incl(DFG_BITSET(set, set_size, block_num), (var_num))
|
||||
|
||||
#define DFG_ISSET(set, set_size, block_num, var_num) \
|
||||
zend_bitset_in(DFG_BITSET(set, set_size, block_num), (var_num))
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg *dfg, uint32_t build_flags);
|
||||
ZEND_API void zend_dfg_add_use_def_op(const zend_op_array *op_array, const zend_op *opline, uint32_t build_flags, zend_bitset use, zend_bitset def);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_DFG_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Bytecode Visualisation |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_DUMP_H
|
||||
#define ZEND_DUMP_H
|
||||
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_dfg.h"
|
||||
|
||||
#define ZEND_DUMP_HIDE_UNREACHABLE (1<<0)
|
||||
#define ZEND_DUMP_RC_INFERENCE (1<<1)
|
||||
#define ZEND_DUMP_CFG (1<<2)
|
||||
#define ZEND_DUMP_SSA (1<<3)
|
||||
#define ZEND_DUMP_LIVE_RANGES (1<<4)
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API void zend_dump_op_array(const zend_op_array *op_array, uint32_t dump_flags, const char *msg, const void *data);
|
||||
ZEND_API void zend_dump_op(const zend_op_array *op_array, const zend_basic_block *b, const zend_op *opline, uint32_t dump_flags, const zend_ssa *ssa, const zend_ssa_op *ssa_op);
|
||||
void zend_dump_dominators(const zend_op_array *op_array, const zend_cfg *cfg);
|
||||
void zend_dump_dfg(const zend_op_array *op_array, const zend_cfg *cfg, const zend_dfg *dfg);
|
||||
void zend_dump_phi_placement(const zend_op_array *op_array, const zend_ssa *ssa);
|
||||
void zend_dump_variables(const zend_op_array *op_array);
|
||||
void zend_dump_ssa_variables(const zend_op_array *op_array, const zend_ssa *ssa, uint32_t dump_flags);
|
||||
ZEND_API void zend_dump_ssa_var(const zend_op_array *op_array, const zend_ssa *ssa, int ssa_var_num, zend_uchar var_type, int var_num, uint32_t dump_flags);
|
||||
ZEND_API void zend_dump_var(const zend_op_array *op_array, zend_uchar var_type, int var_num);
|
||||
void zend_dump_op_array_name(const zend_op_array *op_array);
|
||||
void zend_dump_const(const zval *zv);
|
||||
void zend_dump_ht(HashTable *ht);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_DUMP_H */
|
||||
@@ -1,924 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Func Info |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
| Xinchen Hui <laruence@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "zend_compile.h"
|
||||
#include "zend_extensions.h"
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_optimizer_internal.h"
|
||||
#include "zend_inference.h"
|
||||
#include "zend_call_graph.h"
|
||||
#include "zend_func_info.h"
|
||||
#include "zend_inference.h"
|
||||
#ifdef _WIN32
|
||||
#include "win32/ioutil.h"
|
||||
#endif
|
||||
|
||||
typedef uint32_t (*info_func_t)(const zend_call_info *call_info, const zend_ssa *ssa);
|
||||
|
||||
typedef struct _func_info_t {
|
||||
const char *name;
|
||||
int name_len;
|
||||
uint32_t info;
|
||||
info_func_t info_func;
|
||||
} func_info_t;
|
||||
|
||||
#define F0(name, info) \
|
||||
{name, sizeof(name)-1, (info), NULL}
|
||||
#define F1(name, info) \
|
||||
{name, sizeof(name)-1, (MAY_BE_RC1 | (info)), NULL}
|
||||
#define FN(name, info) \
|
||||
{name, sizeof(name)-1, (MAY_BE_RC1 | MAY_BE_RCN | (info)), NULL}
|
||||
#define FC(name, callback) \
|
||||
{name, sizeof(name)-1, 0, callback}
|
||||
|
||||
static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa)
|
||||
{
|
||||
if (!call_info->send_unpack
|
||||
&& (call_info->num_args == 2 || call_info->num_args == 3)
|
||||
&& ssa
|
||||
&& !(ssa->cfg.flags & ZEND_SSA_TSSA)) {
|
||||
zend_op_array *op_array = call_info->caller_op_array;
|
||||
uint32_t t1 = _ssa_op1_info(op_array, ssa, call_info->arg_info[0].opline,
|
||||
&ssa->ops[call_info->arg_info[0].opline - op_array->opcodes]);
|
||||
uint32_t t2 = _ssa_op1_info(op_array, ssa, call_info->arg_info[1].opline,
|
||||
&ssa->ops[call_info->arg_info[1].opline - op_array->opcodes]);
|
||||
uint32_t t3 = 0;
|
||||
uint32_t tmp = MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED;
|
||||
|
||||
if (call_info->num_args == 3) {
|
||||
t3 = _ssa_op1_info(op_array, ssa, call_info->arg_info[2].opline,
|
||||
&ssa->ops[call_info->arg_info[2].opline - op_array->opcodes]);
|
||||
}
|
||||
if ((t1 & MAY_BE_STRING) && (t2 & MAY_BE_STRING)) {
|
||||
tmp |= MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
|
||||
}
|
||||
if ((t1 & (MAY_BE_DOUBLE|MAY_BE_STRING))
|
||||
|| (t2 & (MAY_BE_DOUBLE|MAY_BE_STRING))
|
||||
|| (t3 & (MAY_BE_DOUBLE|MAY_BE_STRING))) {
|
||||
tmp |= MAY_BE_ARRAY_OF_DOUBLE;
|
||||
}
|
||||
if ((t1 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE))) && (t2 & (MAY_BE_ANY-(MAY_BE_STRING|MAY_BE_DOUBLE)))) {
|
||||
if ((t3 & MAY_BE_ANY) != MAY_BE_DOUBLE) {
|
||||
tmp |= MAY_BE_ARRAY_OF_LONG;
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
} else {
|
||||
/* May throw */
|
||||
return MAY_BE_RC1 | MAY_BE_ARRAY | MAY_BE_ARRAY_PACKED | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
static const func_info_t func_infos[] = {
|
||||
/* zend */
|
||||
F1("zend_version", MAY_BE_STRING),
|
||||
FN("func_get_args", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("get_class_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
|
||||
F1("get_class_methods", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_included_files", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
FN("set_error_handler", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT),
|
||||
F0("restore_error_handler", MAY_BE_TRUE),
|
||||
F0("restore_exception_handler", MAY_BE_TRUE),
|
||||
F1("get_declared_traits", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_declared_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_declared_interfaces", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_defined_functions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("get_defined_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
|
||||
F1("get_resource_type", MAY_BE_STRING),
|
||||
F1("get_defined_constants", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_RESOURCE | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("debug_backtrace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("get_loaded_extensions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_extension_funcs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
|
||||
/* ext/standard */
|
||||
FN("constant", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING | MAY_BE_RESOURCE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("bin2hex", MAY_BE_STRING),
|
||||
F1("hex2bin", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#if HAVE_NANOSLEEP
|
||||
F1("time_nanosleep", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
#endif
|
||||
#if HAVE_STRPTIME
|
||||
F1("strptime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#endif
|
||||
F1("wordwrap", MAY_BE_STRING),
|
||||
F1("htmlspecialchars", MAY_BE_STRING),
|
||||
F1("htmlentities", MAY_BE_STRING),
|
||||
F1("get_html_translation_table", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("sha1", MAY_BE_STRING),
|
||||
F1("sha1_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("md5", MAY_BE_STRING),
|
||||
F1("md5_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iptcparse", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("iptcembed", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("getimagesize", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("getimagesizefromstring", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("image_type_to_mime_type", MAY_BE_STRING),
|
||||
F1("image_type_to_extension", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F0("phpinfo", MAY_BE_TRUE),
|
||||
F1("phpversion", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F0("phpcredits", MAY_BE_TRUE),
|
||||
F1("php_sapi_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("php_uname", MAY_BE_STRING),
|
||||
F1("php_ini_scanned_files", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("php_ini_loaded_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("strtok", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("strrev", MAY_BE_STRING),
|
||||
F1("hebrev", MAY_BE_STRING),
|
||||
F1("basename", MAY_BE_STRING),
|
||||
F1("dirname", MAY_BE_STRING),
|
||||
F1("pathinfo", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stripslashes", MAY_BE_STRING),
|
||||
F1("stripcslashes", MAY_BE_STRING),
|
||||
F1("strstr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stristr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("strrchr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("str_shuffle", MAY_BE_STRING),
|
||||
F1("str_word_count", MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("str_split", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("strpbrk", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
FN("substr_replace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("quotemeta", MAY_BE_STRING),
|
||||
F1("ucwords", MAY_BE_STRING),
|
||||
F1("addcslashes", MAY_BE_STRING),
|
||||
FN("str_replace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
|
||||
FN("str_ireplace", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY | MAY_BE_ARRAY_OF_OBJECT),
|
||||
F1("str_repeat", MAY_BE_STRING),
|
||||
F1("count_chars", MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("chunk_split", MAY_BE_STRING),
|
||||
F1("strip_tags", MAY_BE_STRING),
|
||||
F1("explode", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("localeconv", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#if HAVE_NL_LANGINFO
|
||||
F1("nl_langinfo", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
F1("soundex", MAY_BE_STRING),
|
||||
F1("chr", MAY_BE_STRING),
|
||||
F1("str_getcsv", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("strchr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sprintf", MAY_BE_STRING),
|
||||
F1("vsprintf", MAY_BE_STRING),
|
||||
F1("sscanf", MAY_BE_NULL | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("fscanf", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("parse_url", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("urlencode", MAY_BE_STRING),
|
||||
F1("urldecode", MAY_BE_STRING),
|
||||
F1("rawurlencode", MAY_BE_STRING),
|
||||
F1("rawurldecode", MAY_BE_STRING),
|
||||
F1("http_build_query", MAY_BE_STRING),
|
||||
#if defined(HAVE_SYMLINK) || defined(PHP_WIN32)
|
||||
F1("readlink", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
F1("exec", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("system", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("escapeshellcmd", MAY_BE_STRING),
|
||||
F1("escapeshellarg", MAY_BE_STRING),
|
||||
F0("passthru", MAY_BE_NULL | MAY_BE_FALSE),
|
||||
F1("shell_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#ifdef PHP_CAN_SUPPORT_PROC_OPEN
|
||||
F1("proc_open", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("proc_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#endif
|
||||
F1("random_bytes", MAY_BE_STRING),
|
||||
#if HAVE_GETSERVBYPORT
|
||||
F1("getservbyport", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
#if HAVE_GETPROTOBYNUMBER
|
||||
F1("getprotobynumber", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
F1("base64_decode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("base64_encode", MAY_BE_STRING),
|
||||
F1("password_hash", MAY_BE_STRING),
|
||||
F1("password_get_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("convert_uuencode", MAY_BE_STRING),
|
||||
F1("convert_uudecode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pow", MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_OBJECT),
|
||||
F1("decbin", MAY_BE_STRING),
|
||||
F1("decoct", MAY_BE_STRING),
|
||||
F1("dechex", MAY_BE_STRING),
|
||||
F1("base_convert", MAY_BE_STRING),
|
||||
F1("number_format", MAY_BE_STRING),
|
||||
#ifdef HAVE_INET_NTOP
|
||||
F1("inet_ntop", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
#ifdef HAVE_INET_PTON
|
||||
F1("inet_pton", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
F1("long2ip", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("getenv", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
#ifdef HAVE_PUTENV
|
||||
#endif
|
||||
F1("getopt", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#ifdef HAVE_GETLOADAVG
|
||||
F1("sys_getloadavg", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
|
||||
#endif
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
F1("microtime", MAY_BE_DOUBLE | MAY_BE_STRING),
|
||||
F1("gettimeofday", MAY_BE_DOUBLE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
#endif
|
||||
#ifdef HAVE_GETRUSAGE
|
||||
F1("getrusage", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
#endif
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
F1("uniqid", MAY_BE_STRING),
|
||||
#endif
|
||||
F1("quoted_printable_decode", MAY_BE_STRING),
|
||||
F1("quoted_printable_encode", MAY_BE_STRING),
|
||||
F1("get_current_user", MAY_BE_STRING),
|
||||
F1("get_cfg_var", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("error_get_last", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("serialize", MAY_BE_STRING),
|
||||
F1("var_export", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("print_r", MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F0("register_shutdown_function", MAY_BE_NULL | MAY_BE_FALSE),
|
||||
F1("highlight_file", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("show_source", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("highlight_string", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("php_strip_whitespace", MAY_BE_STRING),
|
||||
F1("ini_get_all", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("ini_alter", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("get_include_path", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("set_include_path", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("headers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("parse_ini_file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("parse_ini_string", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#if ZEND_DEBUG
|
||||
F1("config_get_hash", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#endif
|
||||
F1("gethostbyaddr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gethostbyname", MAY_BE_STRING),
|
||||
F1("gethostbynamel", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#ifdef HAVE_GETHOSTNAME
|
||||
F1("gethostname", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
#if defined(PHP_WIN32) || HAVE_DNS_SEARCH_FUNC
|
||||
# if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS
|
||||
F1("dns_get_record", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
# endif
|
||||
#endif
|
||||
F1("popen", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("fgetc", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fgets", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fread", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("fstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("tempnam", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("tmpfile", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("file_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stream_context_create", MAY_BE_RESOURCE),
|
||||
F1("stream_context_get_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
FN("stream_context_get_options", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
FN("stream_context_get_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
FN("stream_context_set_default", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
FN("stream_filter_prepend", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
FN("stream_filter_append", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("stream_socket_client", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("stream_socket_server", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("stream_socket_accept", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("stream_socket_get_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stream_socket_recvfrom", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#if HAVE_SOCKETPAIR
|
||||
F1("stream_socket_pair", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_RESOURCE),
|
||||
#endif
|
||||
F1("stream_get_contents", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fgetcsv", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("get_meta_tags", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stream_get_meta_data", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("stream_get_line", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stream_get_wrappers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stream_get_transports", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stream_resolve_include_path", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("get_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("socket_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("realpath", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("fsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
FN("pfsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("pack", MAY_BE_STRING),
|
||||
F1("unpack", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("get_browser", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("crypt", MAY_BE_STRING),
|
||||
FN("opendir", MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("getcwd", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("readdir", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("dir", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("scandir", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#ifdef HAVE_GLOB
|
||||
F1("glob", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
#endif
|
||||
F1("filetype", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("stat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("lstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("realpath_cache_get", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
#ifdef HAVE_SYSLOG_H
|
||||
F0("syslog", MAY_BE_TRUE),
|
||||
F0("closelog", MAY_BE_TRUE),
|
||||
#endif
|
||||
F1("metaphone", MAY_BE_STRING),
|
||||
F1("ob_get_flush", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("ob_get_clean", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("ob_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("ob_list_handlers", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F0("array_walk", MAY_BE_TRUE),
|
||||
F0("array_walk_recursive", MAY_BE_TRUE),
|
||||
F0("arsort", MAY_BE_TRUE),
|
||||
F0("asort", MAY_BE_TRUE),
|
||||
F0("krsort", MAY_BE_TRUE),
|
||||
F0("ksort", MAY_BE_TRUE),
|
||||
F0("shuffle", MAY_BE_TRUE),
|
||||
F0("sort", MAY_BE_TRUE),
|
||||
F0("usort", MAY_BE_TRUE),
|
||||
F0("uasort", MAY_BE_TRUE),
|
||||
F0("uksort", MAY_BE_TRUE),
|
||||
F1("compact", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_fill", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_fill_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
FC("range", zend_range_info),
|
||||
F1("array_splice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_slice", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_replace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_replace_recursive", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
FN("array_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
FN("array_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_count_values", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("array_column", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_reverse", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_flip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("array_change_key_case", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
FN("array_rand", MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("array_intersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_intersect_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_intersect_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_uintersect", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_intersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_uintersect_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_intersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_uintersect_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_diff_key", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_diff_ukey", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_udiff", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_diff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_udiff_assoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_diff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_udiff_uassoc", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_filter", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_chunk", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("array_combine", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("str_rot13", MAY_BE_STRING),
|
||||
F1("stream_get_filters", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("stream_bucket_make_writeable", MAY_BE_NULL | MAY_BE_OBJECT),
|
||||
F1("stream_bucket_new", MAY_BE_OBJECT),
|
||||
F1("sys_get_temp_dir", MAY_BE_STRING),
|
||||
|
||||
/* ext/date */
|
||||
F1("date", MAY_BE_STRING),
|
||||
F1("gmdate", MAY_BE_STRING),
|
||||
F1("strftime", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gmstrftime", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("localtime", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("getdate", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("date_create", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_create_immutable", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_create_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_create_immutable_from_format", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_parse", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("date_parse_from_format", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("date_get_last_errors", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("date_format", MAY_BE_STRING),
|
||||
F1("date_timezone_get", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_diff", MAY_BE_OBJECT),
|
||||
F1("timezone_open", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("timezone_name_get", MAY_BE_STRING),
|
||||
F1("timezone_name_from_abbr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("timezone_transitions_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("timezone_location_get", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_DOUBLE | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("timezone_identifiers_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("timezone_abbreviations_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("timezone_version_get", MAY_BE_STRING),
|
||||
F1("date_interval_create_from_date_string", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("date_interval_format", MAY_BE_STRING),
|
||||
F1("date_default_timezone_get", MAY_BE_STRING),
|
||||
F1("date_sunrise", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
|
||||
F1("date_sunset", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_DOUBLE | MAY_BE_STRING),
|
||||
F1("date_sun_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG),
|
||||
|
||||
/* ext/preg */
|
||||
FN("preg_replace", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
|
||||
FN("preg_replace_callback", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("preg_filter", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("preg_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("preg_grep", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
|
||||
/* ext/mysqli */
|
||||
F1("mysqli_connect", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F0("mysqli_close", MAY_BE_TRUE),
|
||||
F1("mysqli_connect_error", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("mysqli_get_client_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mysqli_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("mysqli_get_links_stats", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("mysqli_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT),
|
||||
F1("mysqli_get_charset", MAY_BE_NULL | MAY_BE_OBJECT),
|
||||
F1("mysqli_fetch_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mysqli_fetch_assoc", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mysqli_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mysqli_fetch_object", MAY_BE_NULL | MAY_BE_OBJECT),
|
||||
F1("mysqli_affected_rows", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_character_set_name", MAY_BE_STRING),
|
||||
F0("mysqli_debug", MAY_BE_TRUE),
|
||||
F1("mysqli_error", MAY_BE_STRING),
|
||||
F1("mysqli_reap_async_query", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT),
|
||||
F1("mysqli_stmt_get_result", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_stmt_error_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("mysqli_stmt_get_warnings", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_fetch_field", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_fetch_fields", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_OBJECT),
|
||||
F1("mysqli_fetch_field_direct", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_fetch_lengths", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("mysqli_fetch_row", MAY_BE_NULL | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mysqli_get_client_info", MAY_BE_STRING),
|
||||
F1("mysqli_get_host_info", MAY_BE_STRING),
|
||||
F1("mysqli_get_server_info", MAY_BE_STRING),
|
||||
F1("mysqli_info", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("mysqli_init", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_insert_id", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_num_rows", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_prepare", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_real_escape_string", MAY_BE_STRING),
|
||||
F1("mysqli_stmt_affected_rows", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_stmt_insert_id", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_stmt_num_rows", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mysqli_sqlstate", MAY_BE_STRING),
|
||||
F0("mysqli_ssl_set", MAY_BE_TRUE),
|
||||
F1("mysqli_stat", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mysqli_stmt_error", MAY_BE_STRING),
|
||||
F1("mysqli_stmt_init", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_stmt_result_metadata", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_stmt_sqlstate", MAY_BE_STRING),
|
||||
F1("mysqli_store_result", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("mysqli_use_result", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
|
||||
/* ext/curl */
|
||||
F1("curl_init", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("curl_copy_handle", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("curl_version", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("curl_error", MAY_BE_STRING),
|
||||
F1("curl_strerror", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("curl_multi_strerror", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("curl_escape", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("curl_unescape", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("curl_multi_init", MAY_BE_OBJECT),
|
||||
F1("curl_multi_info_read", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_OBJECT),
|
||||
F1("curl_share_init", MAY_BE_OBJECT),
|
||||
F1("curl_file_create", MAY_BE_OBJECT),
|
||||
|
||||
/* ext/mbstring */
|
||||
F1("mb_convert_case", MAY_BE_STRING),
|
||||
F1("mb_strtoupper", MAY_BE_STRING),
|
||||
F1("mb_strtolower", MAY_BE_STRING),
|
||||
F1("mb_language", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("mb_internal_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("mb_http_input", MAY_BE_FALSE | MAY_BE_STRING| MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_http_output", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("mb_detect_order", MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_substitute_character", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("mb_output_handler", MAY_BE_STRING),
|
||||
F1("mb_preferred_mime_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_strstr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_strrchr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_stristr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_strrichr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_substr", MAY_BE_STRING),
|
||||
F1("mb_strcut", MAY_BE_STRING),
|
||||
F1("mb_strimwidth", MAY_BE_STRING),
|
||||
F1("mb_convert_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("mb_detect_encoding", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_list_encodings", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_encoding_aliases", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_convert_kana", MAY_BE_STRING),
|
||||
F1("mb_encode_mimeheader", MAY_BE_STRING),
|
||||
F1("mb_decode_mimeheader", MAY_BE_STRING),
|
||||
F1("mb_convert_variables", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_encode_numericentity", MAY_BE_STRING),
|
||||
F1("mb_decode_numericentity", MAY_BE_STRING),
|
||||
F1("mb_get_info", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
|
||||
F1("mb_regex_encoding", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("mb_regex_set_options", MAY_BE_STRING),
|
||||
F1("mb_ereg_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_eregi_replace", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_ereg_replace_callback", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mb_split", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_ereg_search_pos", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("mb_ereg_search_regs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("mb_ereg_search_getregs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_STRING),
|
||||
|
||||
/* ext/iconv */
|
||||
F1("iconv", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iconv_get_encoding", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("iconv_substr", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iconv_mime_encode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iconv_mime_decode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("iconv_mime_decode_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
|
||||
/* ext/json */
|
||||
F1("json_encode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("json_decode", MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("json_last_error_msg", MAY_BE_STRING),
|
||||
|
||||
/* ext/xml */
|
||||
F1("xml_error_string", MAY_BE_NULL | MAY_BE_STRING),
|
||||
F1("xml_parser_get_option", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("utf8_encode", MAY_BE_STRING),
|
||||
F1("utf8_decode", MAY_BE_STRING),
|
||||
|
||||
/* ext/zlib */
|
||||
F1("gzgetc", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzgets", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzread", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzopen", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_RESOURCE),
|
||||
F1("gzfile", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("gzcompress", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzuncompress", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzdeflate", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzinflate", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzencode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("gzdecode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("zlib_encode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("zlib_decode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("zlib_get_coding_type", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("ob_gzhandler", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/hash */
|
||||
F1("hash", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("hash_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("hash_hmac", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("hash_hmac_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("hash_hmac_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("hash_hkdf", MAY_BE_STRING),
|
||||
F1("hash_init", MAY_BE_OBJECT),
|
||||
F1("hash_final", MAY_BE_STRING),
|
||||
F1("hash_copy", MAY_BE_OBJECT),
|
||||
F1("hash_algos", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("hash_pbkdf2", MAY_BE_STRING),
|
||||
F1("mhash_keygen_s2k", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mhash_get_hash_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mhash", MAY_BE_FALSE | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/sodium */
|
||||
F1("sodium_crypto_shorthash", MAY_BE_STRING),
|
||||
F1("sodium_crypto_secretbox", MAY_BE_STRING),
|
||||
F1("sodium_crypto_secretbox_open", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_generichash", MAY_BE_STRING),
|
||||
F1("sodium_crypto_generichash_init", MAY_BE_STRING),
|
||||
F0("sodium_crypto_generichash_update", MAY_BE_TRUE),
|
||||
F1("sodium_crypto_generichash_final", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_seed_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_open", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_seal", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_seal_open", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_seed_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_open", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_detached", MAY_BE_STRING),
|
||||
F1("sodium_crypto_stream", MAY_BE_STRING),
|
||||
F1("sodium_crypto_stream_xor", MAY_BE_STRING),
|
||||
F1("sodium_crypto_pwhash", MAY_BE_STRING),
|
||||
F1("sodium_crypto_pwhash_str", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_aes256gcm_encrypt", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_aes256gcm_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_bin2hex", MAY_BE_STRING),
|
||||
F1("sodium_hex2bin", MAY_BE_STRING),
|
||||
F1("sodium_crypto_scalarmult", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_seed_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_keypair", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kx_client_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("sodium_crypto_kx_server_session_keys", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("sodium_crypto_auth", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_aes256gcm_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_auth_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_generichash_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kdf_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_secretbox_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_shorthash_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_stream_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_kdf_derive_from_key", MAY_BE_STRING),
|
||||
F1("sodium_pad", MAY_BE_STRING),
|
||||
F1("sodium_unpad", MAY_BE_STRING),
|
||||
|
||||
F1("sodium_crypto_box_keypair_from_secretkey_and_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_box_publickey_from_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_keypair_from_secretkey_and_publickey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_publickey_from_secretkey", MAY_BE_STRING),
|
||||
F1("sodium_crypto_pwhash_scryptsalsa208sha256", MAY_BE_STRING),
|
||||
F1("sodium_crypto_pwhash_scryptsalsa208sha256_str", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_ed25519_sk_to_curve25519", MAY_BE_STRING),
|
||||
F1("sodium_crypto_sign_ed25519_pk_to_curve25519", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_encrypt", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_ietf_encrypt", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_xchacha20poly1305_ietf_encrypt", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_xchacha20poly1305_ietf_decrypt", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_chacha20poly1305_ietf_keygen", MAY_BE_STRING),
|
||||
F1("sodium_crypto_aead_xchacha20poly1305_ietf_keygen", MAY_BE_STRING),
|
||||
|
||||
/* ext/session */
|
||||
F1("session_get_cookie_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("session_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_module_name", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_save_path", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_create_id", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_cache_limiter", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("session_encode", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/pgsql */
|
||||
F1("pg_dbname", MAY_BE_STRING),
|
||||
F1("pg_options", MAY_BE_STRING),
|
||||
F1("pg_port", MAY_BE_STRING),
|
||||
F1("pg_tty", MAY_BE_STRING),
|
||||
F1("pg_host", MAY_BE_STRING),
|
||||
F1("pg_version", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_NULL),
|
||||
F1("pg_parameter_status", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_query", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_query_params", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_prepare", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_execute", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
FN("pg_last_notice", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY ),
|
||||
F1("pg_field_name", MAY_BE_STRING),
|
||||
F1("pg_field_type_oid", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_fetch_result", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_fetch_row", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_fetch_assoc", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_fetch_array", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_fetch_object", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_fetch_all", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("pg_fetch_all_columns", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_NULL | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_last_oid", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_lo_create", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_lo_open", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_lo_read", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_lo_import", MAY_BE_FALSE | MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_copy_to", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("pg_escape_string", MAY_BE_STRING),
|
||||
F1("pg_escape_bytea", MAY_BE_STRING),
|
||||
F1("pg_unescape_bytea", MAY_BE_STRING),
|
||||
F1("pg_escape_literal", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_escape_identifier", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_result_error", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_result_error_field", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("pg_get_result", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_result_status", MAY_BE_LONG | MAY_BE_STRING),
|
||||
F1("pg_get_notify", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("pg_socket", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("pg_meta_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY),
|
||||
F1("pg_convert", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("pg_insert", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT | MAY_BE_STRING),
|
||||
F1("pg_update", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("pg_delete", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_STRING),
|
||||
F1("pg_select", MAY_BE_FALSE | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY),
|
||||
|
||||
/* ext/bcmath */
|
||||
F1("bcadd", MAY_BE_STRING),
|
||||
F1("bcsub", MAY_BE_STRING),
|
||||
F1("bcmul", MAY_BE_STRING),
|
||||
F1("bcdiv", MAY_BE_STRING),
|
||||
F1("bcmod", MAY_BE_STRING),
|
||||
F1("bcpowmod", MAY_BE_STRING),
|
||||
F1("bcpow", MAY_BE_STRING),
|
||||
F1("bcsqrt", MAY_BE_STRING),
|
||||
|
||||
/* ext/exif */
|
||||
F1("exif_tagname", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("exif_read_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("exif_thumbnail", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/filter */
|
||||
F1("filter_input_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("filter_var_array", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF),
|
||||
F1("filter_list", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING),
|
||||
|
||||
/* ext/gettext */
|
||||
F1("textdomain", MAY_BE_STRING),
|
||||
F1("gettext", MAY_BE_STRING),
|
||||
F1("_", MAY_BE_STRING),
|
||||
F1("dgettext", MAY_BE_STRING),
|
||||
F1("dcgettext", MAY_BE_STRING),
|
||||
F1("bindtextdomain", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#if HAVE_NGETTEXT
|
||||
F1("ngettext", MAY_BE_STRING),
|
||||
#endif
|
||||
#if HAVE_DNGETTEXT
|
||||
F1("dcngettext", MAY_BE_STRING),
|
||||
#endif
|
||||
#if HAVE_BIND_TEXTDOMAIN_CODESET
|
||||
F1("bind_textdomain_codeset", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
#endif
|
||||
|
||||
/* ext/fileinfo */
|
||||
F1("finfo_open", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("finfo_file", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("finfo_buffer", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
F1("mime_content_type", MAY_BE_FALSE | MAY_BE_STRING),
|
||||
|
||||
/* ext/gd */
|
||||
F1("gd_info", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE),
|
||||
F1("imagecreatetruecolor", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#ifdef PHP_WIN32
|
||||
F1("imagegrabwindow", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagegrabscreen", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
F1("imagerotate", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreate", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromstring", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromgif", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#ifdef HAVE_GD_JPG
|
||||
F1("imagecreatefromjpeg", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
#ifdef HAVE_GD_PNG
|
||||
F1("imagecreatefrompng", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
#ifdef HAVE_GD_WEBP
|
||||
F1("imagecreatefromwebp", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
F1("imagecreatefromxbm", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#if defined(HAVE_GD_XPM)
|
||||
F1("imagecreatefromxpm", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
F1("imagecreatefromwbmp", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromgd", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromgd2", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecreatefromgd2part", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#if defined(HAVE_GD_BMP)
|
||||
F1("imagecreatefrombmp", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
#endif
|
||||
F0("imagecolorset", MAY_BE_NULL | MAY_BE_FALSE),
|
||||
F1("imagecolorsforindex", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagegetclip", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imageftbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagefttext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagettfbbox", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagettftext", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
F1("imagecrop", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagecropauto", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imagescale", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imageaffine", MAY_BE_FALSE | MAY_BE_OBJECT),
|
||||
F1("imageaffinematrixget", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
|
||||
F1("imageaffinematrixconcat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_DOUBLE),
|
||||
F1("imageresolution", MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_LONG),
|
||||
|
||||
/* ext/spl */
|
||||
F1("class_implements", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("class_parents", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("class_uses", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("iterator_to_array", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_ARRAY_OF_ANY),
|
||||
F1("spl_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_STRING),
|
||||
F1("spl_object_hash", MAY_BE_STRING),
|
||||
|
||||
};
|
||||
|
||||
static HashTable func_info;
|
||||
ZEND_API int zend_func_info_rid = -1;
|
||||
|
||||
static uint32_t get_internal_func_info(
|
||||
const zend_call_info *call_info, const zend_ssa *ssa) {
|
||||
zend_function *callee_func = call_info->callee_func;
|
||||
if (callee_func->common.scope) {
|
||||
/* This is a method, not a function. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
zval *zv = zend_hash_find_ex(&func_info, callee_func->common.function_name, 1);
|
||||
if (!zv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
func_info_t *info = Z_PTR_P(zv);
|
||||
if (info->info_func) {
|
||||
return info->info_func(call_info, ssa);
|
||||
} else {
|
||||
return info->info;
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API uint32_t zend_get_func_info(
|
||||
const zend_call_info *call_info, const zend_ssa *ssa,
|
||||
zend_class_entry **ce, bool *ce_is_instanceof)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
const zend_function *callee_func = call_info->callee_func;
|
||||
*ce = NULL;
|
||||
*ce_is_instanceof = 0;
|
||||
|
||||
if (callee_func->type == ZEND_INTERNAL_FUNCTION) {
|
||||
uint32_t internal_ret = get_internal_func_info(call_info, ssa);
|
||||
#if !ZEND_DEBUG
|
||||
if (internal_ret) {
|
||||
return internal_ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = zend_get_return_info_from_signature_only(
|
||||
callee_func, /* script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);
|
||||
|
||||
#if ZEND_DEBUG
|
||||
if (internal_ret) {
|
||||
zend_string *name = callee_func->common.function_name;
|
||||
/* Check whether the func_info information is a subset of the information we can
|
||||
* compute from the specified return type, otherwise it contains redundant types. */
|
||||
if (internal_ret & ~ret) {
|
||||
fprintf(stderr, "Inaccurate func info for %s()\n", ZSTR_VAL(name));
|
||||
}
|
||||
/* Check whether the func info is completely redundant with arginfo. */
|
||||
if (internal_ret == ret) {
|
||||
fprintf(stderr, "Useless func info for %s()\n", ZSTR_VAL(name));
|
||||
}
|
||||
/* If the return type is not mixed, check that the types match exactly if we exclude
|
||||
* RC and array information. */
|
||||
uint32_t ret_any = ret & MAY_BE_ANY, internal_ret_any = internal_ret & MAY_BE_ANY;
|
||||
if (ret_any != MAY_BE_ANY) {
|
||||
uint32_t diff = internal_ret_any ^ ret_any;
|
||||
/* Func info may contain "true" types as well as isolated "null" and "false". */
|
||||
if (diff && !(diff == MAY_BE_FALSE && (ret & MAY_BE_FALSE))
|
||||
&& (internal_ret_any & ~(MAY_BE_NULL|MAY_BE_FALSE))) {
|
||||
fprintf(stderr, "Incorrect func info for %s()\n", ZSTR_VAL(name));
|
||||
}
|
||||
}
|
||||
return internal_ret;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (!call_info->is_prototype) {
|
||||
// FIXME: the order of functions matters!!!
|
||||
zend_func_info *info = ZEND_FUNC_INFO((zend_op_array*)callee_func);
|
||||
if (info) {
|
||||
ret = info->return_info.type;
|
||||
*ce = info->return_info.ce;
|
||||
*ce_is_instanceof = info->return_info.is_instanceof;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
ret = zend_get_return_info_from_signature_only(
|
||||
callee_func, /* TODO: script */ NULL, ce, ce_is_instanceof, /* use_tentative_return_info */ !call_info->is_prototype);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int zend_func_info_startup(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (zend_func_info_rid == -1) {
|
||||
zend_func_info_rid = zend_get_resource_handle("Zend Optimizer");
|
||||
if (zend_func_info_rid < 0) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
zend_hash_init(&func_info, sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1);
|
||||
for (i = 0; i < sizeof(func_infos)/sizeof(func_info_t); i++) {
|
||||
zend_string *key = zend_string_init_interned(func_infos[i].name, func_infos[i].name_len, 1);
|
||||
|
||||
if (zend_hash_add_ptr(&func_info, key, (void**)&func_infos[i]) == NULL) {
|
||||
fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name);
|
||||
}
|
||||
zend_string_release_ex(key, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int zend_func_info_shutdown(void)
|
||||
{
|
||||
if (zend_func_info_rid != -1) {
|
||||
zend_hash_destroy(&func_info);
|
||||
zend_func_info_rid = -1;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, Func Info |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_FUNC_INFO_H
|
||||
#define ZEND_FUNC_INFO_H
|
||||
|
||||
#include "zend_ssa.h"
|
||||
|
||||
/* func/cfg flags */
|
||||
#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
|
||||
#define ZEND_FUNC_HAS_CALLS (1<<1)
|
||||
#define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
|
||||
#define ZEND_FUNC_NO_LOOPS (1<<3)
|
||||
#define ZEND_FUNC_IRREDUCIBLE (1<<4)
|
||||
#define ZEND_FUNC_FREE_LOOP_VAR (1<<5)
|
||||
#define ZEND_FUNC_RECURSIVE (1<<7)
|
||||
#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
|
||||
#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
|
||||
#define ZEND_FUNC_HAS_EXTENDED_FCALL (1<<10)
|
||||
#define ZEND_FUNC_HAS_EXTENDED_STMT (1<<11)
|
||||
#define ZEND_SSA_TSSA (1<<12) /* used by tracing JIT */
|
||||
|
||||
#define ZEND_FUNC_JIT_ON_FIRST_EXEC (1<<13) /* used by JIT */
|
||||
#define ZEND_FUNC_JIT_ON_PROF_REQUEST (1<<14) /* used by JIT */
|
||||
#define ZEND_FUNC_JIT_ON_HOT_COUNTERS (1<<15) /* used by JIT */
|
||||
#define ZEND_FUNC_JIT_ON_HOT_TRACE (1<<16) /* used by JIT */
|
||||
|
||||
|
||||
typedef struct _zend_func_info zend_func_info;
|
||||
typedef struct _zend_call_info zend_call_info;
|
||||
|
||||
#define ZEND_FUNC_INFO(op_array) \
|
||||
((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
|
||||
|
||||
#define ZEND_SET_FUNC_INFO(op_array, info) do { \
|
||||
zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
|
||||
*pinfo = info; \
|
||||
} while (0)
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
extern ZEND_API int zend_func_info_rid;
|
||||
|
||||
ZEND_API uint32_t zend_get_func_info(
|
||||
const zend_call_info *call_info, const zend_ssa *ssa,
|
||||
zend_class_entry **ce, bool *ce_is_instanceof);
|
||||
|
||||
int zend_func_info_startup(void);
|
||||
int zend_func_info_shutdown(void);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_FUNC_INFO_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,291 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, e-SSA based Type & Range Inference |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_INFERENCE_H
|
||||
#define ZEND_INFERENCE_H
|
||||
|
||||
#include "zend_optimizer.h"
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
/* Bitmask for type inference (zend_ssa_var_info.type) */
|
||||
#include "zend_type_info.h"
|
||||
|
||||
#define MAY_BE_PACKED_GUARD (1<<27) /* needs packed array guard */
|
||||
#define MAY_BE_CLASS_GUARD (1<<27) /* needs class guard */
|
||||
#define MAY_BE_GUARD (1<<28) /* needs type guard */
|
||||
//#define MAY_BE_IN_REG (1<<29) /* deprecated and not used */
|
||||
|
||||
//TODO: remome MAY_BE_RC1, MAY_BE_RCN???
|
||||
#define MAY_BE_RC1 (1<<30) /* may be non-reference with refcount == 1 */
|
||||
#define MAY_BE_RCN (1u<<31) /* may be non-reference with refcount > 1 */
|
||||
|
||||
#define MAY_HAVE_DTOR \
|
||||
(MAY_BE_OBJECT|MAY_BE_RESOURCE \
|
||||
|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE)
|
||||
|
||||
#define DEFINE_SSA_OP_HAS_RANGE(opN) \
|
||||
static zend_always_inline bool _ssa_##opN##_has_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
return (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL); \
|
||||
} else { \
|
||||
return (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_MIN_RANGE(opN) \
|
||||
static zend_always_inline zend_long _ssa_##opN##_min_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
if (Z_TYPE_P(zv) == IS_LONG) { \
|
||||
return Z_LVAL_P(zv); \
|
||||
} else if (Z_TYPE_P(zv) == IS_TRUE) { \
|
||||
return 1; \
|
||||
} else if (Z_TYPE_P(zv) == IS_FALSE) { \
|
||||
return 0; \
|
||||
} else if (Z_TYPE_P(zv) == IS_NULL) { \
|
||||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.min; \
|
||||
} \
|
||||
return ZEND_LONG_MIN; \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_MAX_RANGE(opN) \
|
||||
static zend_always_inline zend_long _ssa_##opN##_max_range(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
if (Z_TYPE_P(zv) == IS_LONG) { \
|
||||
return Z_LVAL_P(zv); \
|
||||
} else if (Z_TYPE_P(zv) == IS_TRUE) { \
|
||||
return 1; \
|
||||
} else if (Z_TYPE_P(zv) == IS_FALSE) { \
|
||||
return 0; \
|
||||
} else if (Z_TYPE_P(zv) == IS_NULL) { \
|
||||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.max; \
|
||||
} \
|
||||
return ZEND_LONG_MAX; \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_RANGE_UNDERFLOW(opN) \
|
||||
static zend_always_inline char _ssa_##opN##_range_underflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
|
||||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.underflow; \
|
||||
} \
|
||||
return 1; \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_RANGE_OVERFLOW(opN) \
|
||||
static zend_always_inline char _ssa_##opN##_range_overflow(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
zval *zv = CRT_CONSTANT(opline->opN); \
|
||||
if (Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE || Z_TYPE_P(zv) == IS_NULL) { \
|
||||
return 0; \
|
||||
} \
|
||||
} else if (opline->opN##_type != IS_UNUSED && \
|
||||
ssa->var_info && \
|
||||
ssa_op->opN##_use >= 0 && \
|
||||
ssa->var_info[ssa_op->opN##_use].has_range) { \
|
||||
return ssa->var_info[ssa_op->opN##_use].range.overflow; \
|
||||
} \
|
||||
return 1; \
|
||||
} \
|
||||
|
||||
DEFINE_SSA_OP_HAS_RANGE(op1)
|
||||
DEFINE_SSA_OP_MIN_RANGE(op1)
|
||||
DEFINE_SSA_OP_MAX_RANGE(op1)
|
||||
DEFINE_SSA_OP_RANGE_UNDERFLOW(op1)
|
||||
DEFINE_SSA_OP_RANGE_OVERFLOW(op1)
|
||||
DEFINE_SSA_OP_HAS_RANGE(op2)
|
||||
DEFINE_SSA_OP_MIN_RANGE(op2)
|
||||
DEFINE_SSA_OP_MAX_RANGE(op2)
|
||||
DEFINE_SSA_OP_RANGE_UNDERFLOW(op2)
|
||||
DEFINE_SSA_OP_RANGE_OVERFLOW(op2)
|
||||
|
||||
#define OP1_HAS_RANGE() (_ssa_op1_has_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_MIN_RANGE() (_ssa_op1_min_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_MAX_RANGE() (_ssa_op1_max_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_RANGE_UNDERFLOW() (_ssa_op1_range_underflow (op_array, ssa, opline, ssa_op))
|
||||
#define OP1_RANGE_OVERFLOW() (_ssa_op1_range_overflow (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_HAS_RANGE() (_ssa_op2_has_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_MIN_RANGE() (_ssa_op2_min_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_MAX_RANGE() (_ssa_op2_max_range (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_RANGE_UNDERFLOW() (_ssa_op2_range_underflow (op_array, ssa, opline, ssa_op))
|
||||
#define OP2_RANGE_OVERFLOW() (_ssa_op2_range_overflow (op_array, ssa, opline, ssa_op))
|
||||
|
||||
static zend_always_inline uint32_t _const_op_type(const zval *zv) {
|
||||
if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
|
||||
return MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY;
|
||||
} else if (Z_TYPE_P(zv) == IS_ARRAY) {
|
||||
HashTable *ht = Z_ARRVAL_P(zv);
|
||||
uint32_t tmp = MAY_BE_ARRAY;
|
||||
zend_string *str;
|
||||
zval *val;
|
||||
|
||||
if (Z_REFCOUNTED_P(zv)) {
|
||||
tmp |= MAY_BE_RC1 | MAY_BE_RCN;
|
||||
} else {
|
||||
tmp |= MAY_BE_RCN;
|
||||
}
|
||||
|
||||
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str, val) {
|
||||
if (str) {
|
||||
tmp |= MAY_BE_ARRAY_KEY_STRING;
|
||||
} else {
|
||||
tmp |= MAY_BE_ARRAY_KEY_LONG;
|
||||
}
|
||||
tmp |= 1 << (Z_TYPE_P(val) + MAY_BE_ARRAY_SHIFT);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
if (HT_IS_PACKED(ht)) {
|
||||
tmp &= ~MAY_BE_ARRAY_HASH;
|
||||
}
|
||||
return tmp;
|
||||
} else {
|
||||
uint32_t tmp = (1 << Z_TYPE_P(zv));
|
||||
|
||||
if (Z_REFCOUNTED_P(zv)) {
|
||||
tmp |= MAY_BE_RC1 | MAY_BE_RCN;
|
||||
} else if (Z_TYPE_P(zv) == IS_STRING) {
|
||||
tmp |= MAY_BE_RCN;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline uint32_t get_ssa_var_info(const zend_ssa *ssa, int ssa_var_num)
|
||||
{
|
||||
if (ssa->var_info && ssa_var_num >= 0) {
|
||||
return ssa->var_info[ssa_var_num].type;
|
||||
} else {
|
||||
return MAY_BE_UNDEF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_REF | MAY_BE_INDIRECT | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF;
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFINE_SSA_OP_INFO(opN) \
|
||||
static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
if (opline->opN##_type == IS_CONST) { \
|
||||
return _const_op_type(CRT_CONSTANT(opline->opN)); \
|
||||
} else { \
|
||||
return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_use : -1); \
|
||||
} \
|
||||
} \
|
||||
|
||||
#define DEFINE_SSA_OP_DEF_INFO(opN) \
|
||||
static zend_always_inline uint32_t _ssa_##opN##_def_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
|
||||
{ \
|
||||
return get_ssa_var_info(ssa, ssa->var_info ? ssa_op->opN##_def : -1); \
|
||||
} \
|
||||
|
||||
|
||||
DEFINE_SSA_OP_INFO(op1)
|
||||
DEFINE_SSA_OP_INFO(op2)
|
||||
DEFINE_SSA_OP_INFO(result)
|
||||
DEFINE_SSA_OP_DEF_INFO(op1)
|
||||
DEFINE_SSA_OP_DEF_INFO(op2)
|
||||
DEFINE_SSA_OP_DEF_INFO(result)
|
||||
|
||||
#define OP1_INFO() (_ssa_op1_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP2_INFO() (_ssa_op2_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define RES_USE_INFO() (_ssa_result_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP1_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP2_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, opline, ssa_op))
|
||||
#define OP1_DATA_DEF_INFO() (_ssa_op1_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define OP2_DATA_DEF_INFO() (_ssa_op2_def_info(op_array, ssa, (opline+1), (ssa_op+1)))
|
||||
#define RES_INFO() (_ssa_result_def_info(op_array, ssa, opline, ssa_op))
|
||||
|
||||
static zend_always_inline bool zend_add_will_overflow(zend_long a, zend_long b) {
|
||||
return (b > 0 && a > ZEND_LONG_MAX - b)
|
||||
|| (b < 0 && a < ZEND_LONG_MIN - b);
|
||||
}
|
||||
static zend_always_inline bool zend_sub_will_overflow(zend_long a, zend_long b) {
|
||||
return (b > 0 && a < ZEND_LONG_MIN + b)
|
||||
|| (b < 0 && a > ZEND_LONG_MAX + b);
|
||||
}
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API int zend_ssa_find_false_dependencies(const zend_op_array *op_array, zend_ssa *ssa);
|
||||
ZEND_API int zend_ssa_find_sccs(const zend_op_array *op_array, zend_ssa *ssa);
|
||||
ZEND_API int zend_ssa_inference(zend_arena **raena, const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_long optimization_level);
|
||||
|
||||
ZEND_API uint32_t zend_array_element_type(uint32_t t1, zend_uchar op_type, int write, int insert);
|
||||
|
||||
int zend_inference_calc_range(const zend_op_array *op_array, zend_ssa *ssa, int var, int widening, int narrowing, zend_ssa_range *tmp);
|
||||
ZEND_API int zend_inference_propagate_range(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, zend_ssa_op* ssa_op, int var, zend_ssa_range *tmp);
|
||||
void zend_inference_init_range(const zend_op_array *op_array, zend_ssa *ssa, int var, bool underflow, zend_long min, zend_long max, bool overflow);
|
||||
int zend_inference_narrowing_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
|
||||
int zend_inference_widening_meet(zend_ssa_var_info *var_info, zend_ssa_range *r);
|
||||
void zend_inference_check_recursive_dependencies(zend_op_array *op_array);
|
||||
|
||||
int zend_infer_types_ex(const zend_op_array *op_array, const zend_script *script, zend_ssa *ssa, zend_bitset worklist, zend_long optimization_level);
|
||||
|
||||
ZEND_API uint32_t zend_fetch_arg_info_type(
|
||||
const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce);
|
||||
ZEND_API void zend_init_func_return_info(
|
||||
const zend_op_array *op_array, const zend_script *script, zend_ssa_var_info *ret);
|
||||
uint32_t zend_get_return_info_from_signature_only(
|
||||
const zend_function *func, const zend_script *script,
|
||||
zend_class_entry **ce, bool *ce_is_instanceof, bool use_tentative_return_info);
|
||||
void zend_func_return_info(const zend_op_array *op_array,
|
||||
const zend_script *script,
|
||||
int recursive,
|
||||
int widening,
|
||||
zend_ssa_var_info *ret);
|
||||
|
||||
ZEND_API int zend_may_throw_ex(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa, uint32_t t1, uint32_t t2);
|
||||
ZEND_API int zend_may_throw(const zend_op *opline, const zend_ssa_op *ssa_op, const zend_op_array *op_array, zend_ssa *ssa);
|
||||
|
||||
ZEND_API int zend_update_type_info(
|
||||
const zend_op_array *op_array, zend_ssa *ssa, const zend_script *script,
|
||||
zend_op *opline, zend_ssa_op *ssa_op, const zend_op **ssa_opcodes,
|
||||
zend_long optimization_level);
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif /* ZEND_INFERENCE_H */
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_OPTIMIZER_H
|
||||
#define ZEND_OPTIMIZER_H
|
||||
|
||||
#include "zend.h"
|
||||
#include "zend_compile.h"
|
||||
|
||||
#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* Simple local optimizations */
|
||||
#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* */
|
||||
#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* Jump optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_4 (1<<3) /* INIT_FCALL_BY_NAME -> DO_FCALL */
|
||||
#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_6 (1<<5) /* DFA based optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_7 (1<<6) /* CALL GRAPH optimization */
|
||||
#define ZEND_OPTIMIZER_PASS_8 (1<<7) /* SCCP (constant propagation) */
|
||||
#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) /* Merge equal constants */
|
||||
#define ZEND_OPTIMIZER_PASS_12 (1<<11) /* Adjust used stack */
|
||||
#define ZEND_OPTIMIZER_PASS_13 (1<<12) /* Remove unused variables */
|
||||
#define ZEND_OPTIMIZER_PASS_14 (1<<13) /* DCE (dead code elimination) */
|
||||
#define ZEND_OPTIMIZER_PASS_15 (1<<14) /* (unsafe) Collect constants */
|
||||
#define ZEND_OPTIMIZER_PASS_16 (1<<15) /* Inline functions */
|
||||
|
||||
#define ZEND_OPTIMIZER_IGNORE_OVERLOADING (1<<16) /* (unsafe) Ignore possibility of operator overloading */
|
||||
|
||||
#define ZEND_OPTIMIZER_ALL_PASSES 0x7FFFFFFF
|
||||
|
||||
#define DEFAULT_OPTIMIZATION_LEVEL "0x7FFEBFFF"
|
||||
|
||||
|
||||
#define ZEND_DUMP_AFTER_PASS_1 ZEND_OPTIMIZER_PASS_1
|
||||
#define ZEND_DUMP_AFTER_PASS_2 ZEND_OPTIMIZER_PASS_2
|
||||
#define ZEND_DUMP_AFTER_PASS_3 ZEND_OPTIMIZER_PASS_3
|
||||
#define ZEND_DUMP_AFTER_PASS_4 ZEND_OPTIMIZER_PASS_4
|
||||
#define ZEND_DUMP_AFTER_PASS_5 ZEND_OPTIMIZER_PASS_5
|
||||
#define ZEND_DUMP_AFTER_PASS_6 ZEND_OPTIMIZER_PASS_6
|
||||
#define ZEND_DUMP_AFTER_PASS_7 ZEND_OPTIMIZER_PASS_7
|
||||
#define ZEND_DUMP_AFTER_PASS_8 ZEND_OPTIMIZER_PASS_8
|
||||
#define ZEND_DUMP_AFTER_PASS_9 ZEND_OPTIMIZER_PASS_9
|
||||
#define ZEND_DUMP_AFTER_PASS_10 ZEND_OPTIMIZER_PASS_10
|
||||
#define ZEND_DUMP_AFTER_PASS_11 ZEND_OPTIMIZER_PASS_11
|
||||
#define ZEND_DUMP_AFTER_PASS_12 ZEND_OPTIMIZER_PASS_12
|
||||
#define ZEND_DUMP_AFTER_PASS_13 ZEND_OPTIMIZER_PASS_13
|
||||
#define ZEND_DUMP_AFTER_PASS_14 ZEND_OPTIMIZER_PASS_14
|
||||
|
||||
#define ZEND_DUMP_BEFORE_OPTIMIZER (1<<16)
|
||||
#define ZEND_DUMP_AFTER_OPTIMIZER (1<<17)
|
||||
|
||||
#define ZEND_DUMP_BEFORE_BLOCK_PASS (1<<18)
|
||||
#define ZEND_DUMP_AFTER_BLOCK_PASS (1<<19)
|
||||
#define ZEND_DUMP_BLOCK_PASS_VARS (1<<20)
|
||||
|
||||
#define ZEND_DUMP_BEFORE_DFA_PASS (1<<21)
|
||||
#define ZEND_DUMP_AFTER_DFA_PASS (1<<22)
|
||||
#define ZEND_DUMP_DFA_CFG (1<<23)
|
||||
#define ZEND_DUMP_DFA_DOMINATORS (1<<24)
|
||||
#define ZEND_DUMP_DFA_LIVENESS (1<<25)
|
||||
#define ZEND_DUMP_DFA_PHI (1<<26)
|
||||
#define ZEND_DUMP_DFA_SSA (1<<27)
|
||||
#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
|
||||
#define ZEND_DUMP_SCCP (1<<29)
|
||||
|
||||
typedef struct _zend_script {
|
||||
zend_string *filename;
|
||||
zend_op_array main_op_array;
|
||||
HashTable function_table;
|
||||
HashTable class_table;
|
||||
uint32_t first_early_binding_opline; /* the linked list of delayed declarations */
|
||||
} zend_script;
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
ZEND_API int zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level);
|
||||
int zend_optimizer_startup(void);
|
||||
int zend_optimizer_shutdown(void);
|
||||
END_EXTERN_C()
|
||||
|
||||
#endif
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend OPcache |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://www.php.net/license/3_01.txt |
|
||||
| If you did not receive a copy of the PHP license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Andi Gutmans <andi@php.net> |
|
||||
| Zeev Suraski <zeev@php.net> |
|
||||
| Stanislav Malyshev <stas@zend.com> |
|
||||
| Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_OPTIMIZER_INTERNAL_H
|
||||
#define ZEND_OPTIMIZER_INTERNAL_H
|
||||
|
||||
#include "zend_ssa.h"
|
||||
#include "zend_func_info.h"
|
||||
|
||||
#define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant]
|
||||
#define ZEND_OP1_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op1)
|
||||
#define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant]
|
||||
#define ZEND_OP2_JMP_ADDR(opline) OP_JMP_ADDR(opline, (opline)->op2)
|
||||
|
||||
#define VAR_NUM(v) EX_VAR_TO_NUM(v)
|
||||
#define NUM_VAR(v) EX_NUM_TO_VAR(v)
|
||||
|
||||
#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)
|
||||
|
||||
#define RESULT_UNUSED(op) (op->result_type == IS_UNUSED)
|
||||
#define SAME_VAR(op1, op2) (op1 ## _type == op2 ## _type && op1.var == op2.var)
|
||||
|
||||
typedef struct _zend_optimizer_ctx {
|
||||
zend_arena *arena;
|
||||
zend_script *script;
|
||||
HashTable *constants;
|
||||
zend_long optimization_level;
|
||||
zend_long debug_level;
|
||||
} zend_optimizer_ctx;
|
||||
|
||||
#define LITERAL_LONG(op, val) do { \
|
||||
zval _c; \
|
||||
ZVAL_LONG(&_c, val); \
|
||||
op.constant = zend_optimizer_add_literal(op_array, &_c); \
|
||||
} while (0)
|
||||
|
||||
#define LITERAL_BOOL(op, val) do { \
|
||||
zval _c; \
|
||||
ZVAL_BOOL(&_c, val); \
|
||||
op.constant = zend_optimizer_add_literal(op_array, &_c); \
|
||||
} while (0)
|
||||
|
||||
#define literal_dtor(zv) do { \
|
||||
zval_ptr_dtor_nogc(zv); \
|
||||
ZVAL_NULL(zv); \
|
||||
} while (0)
|
||||
|
||||
#define COPY_NODE(target, src) do { \
|
||||
target ## _type = src ## _type; \
|
||||
target = src; \
|
||||
} while (0)
|
||||
|
||||
static inline bool zend_optimizer_is_loop_var_free(const zend_op *opline) {
|
||||
return (opline->opcode == ZEND_FE_FREE && opline->extended_value != ZEND_FREE_ON_RETURN)
|
||||
|| (opline->opcode == ZEND_FREE && opline->extended_value == ZEND_FREE_SWITCH);
|
||||
}
|
||||
|
||||
int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv);
|
||||
int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int copy);
|
||||
void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value);
|
||||
int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value);
|
||||
int zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval *op1, zval *op2);
|
||||
int zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1);
|
||||
int zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1);
|
||||
int zend_optimizer_eval_strlen(zval *result, zval *op1);
|
||||
int zend_optimizer_update_op1_const(zend_op_array *op_array,
|
||||
zend_op *opline,
|
||||
zval *val);
|
||||
int zend_optimizer_update_op2_const(zend_op_array *op_array,
|
||||
zend_op *opline,
|
||||
zval *val);
|
||||
int zend_optimizer_replace_by_const(zend_op_array *op_array,
|
||||
zend_op *opline,
|
||||
zend_uchar type,
|
||||
uint32_t var,
|
||||
zval *val);
|
||||
zend_op *zend_optimizer_get_loop_var_def(const zend_op_array *op_array, zend_op *free_opline);
|
||||
|
||||
void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimizer_pass3(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimize_func_calls(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimize_cfg(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimize_dfa(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
int zend_dfa_analyze_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa);
|
||||
void zend_dfa_optimize_op_array(zend_op_array *op_array, zend_optimizer_ctx *ctx, zend_ssa *ssa, zend_call_info **call_map);
|
||||
void zend_optimize_temporary_variables(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimizer_nop_removal(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx *ctx);
|
||||
void zend_optimizer_compact_vars(zend_op_array *op_array);
|
||||
zend_function *zend_optimizer_get_called_func(
|
||||
zend_script *script, zend_op_array *op_array, zend_op *opline, bool *is_prototype);
|
||||
uint32_t zend_optimizer_classify_function(zend_string *name, uint32_t num_args);
|
||||
void zend_optimizer_migrate_jump(zend_op_array *op_array, zend_op *new_opline, zend_op *opline);
|
||||
void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_t *shiftlist);
|
||||
int sccp_optimize_op_array(zend_optimizer_ctx *ctx, zend_op_array *op_array, zend_ssa *ssa, zend_call_info **call_map);
|
||||
int dce_optimize_op_array(zend_op_array *op_array, zend_ssa *ssa, bool reorder_dtor_effects);
|
||||
int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, zend_ssa *ssa);
|
||||
|
||||
typedef void (*zend_op_array_func_t)(zend_op_array *, void *context);
|
||||
void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,326 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine, SSA - Static Single Assignment Form |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Dmitry Stogov <dmitry@php.net> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_SSA_H
|
||||
#define ZEND_SSA_H
|
||||
|
||||
#include "zend_optimizer.h"
|
||||
#include "zend_cfg.h"
|
||||
|
||||
typedef struct _zend_ssa_range {
|
||||
zend_long min;
|
||||
zend_long max;
|
||||
bool underflow;
|
||||
bool overflow;
|
||||
} zend_ssa_range;
|
||||
|
||||
typedef enum _zend_ssa_negative_lat {
|
||||
NEG_NONE = 0,
|
||||
NEG_INIT = 1,
|
||||
NEG_INVARIANT = 2,
|
||||
NEG_USE_LT = 3,
|
||||
NEG_USE_GT = 4,
|
||||
NEG_UNKNOWN = 5
|
||||
} zend_ssa_negative_lat;
|
||||
|
||||
/* Special kind of SSA Phi function used in eSSA */
|
||||
typedef struct _zend_ssa_range_constraint {
|
||||
zend_ssa_range range; /* simple range constraint */
|
||||
int min_var;
|
||||
int max_var;
|
||||
int min_ssa_var; /* ((min_var>0) ? MIN(ssa_var) : 0) + range.min */
|
||||
int max_ssa_var; /* ((max_var>0) ? MAX(ssa_var) : 0) + range.max */
|
||||
zend_ssa_negative_lat negative;
|
||||
} zend_ssa_range_constraint;
|
||||
|
||||
typedef struct _zend_ssa_type_constraint {
|
||||
uint32_t type_mask; /* Type mask to intersect with */
|
||||
zend_class_entry *ce; /* Class entry for instanceof constraints */
|
||||
} zend_ssa_type_constraint;
|
||||
|
||||
typedef union _zend_ssa_pi_constraint {
|
||||
zend_ssa_range_constraint range;
|
||||
zend_ssa_type_constraint type;
|
||||
} zend_ssa_pi_constraint;
|
||||
|
||||
/* SSA Phi - ssa_var = Phi(source0, source1, ...sourceN) */
|
||||
typedef struct _zend_ssa_phi zend_ssa_phi;
|
||||
struct _zend_ssa_phi {
|
||||
zend_ssa_phi *next; /* next Phi in the same BB */
|
||||
int pi; /* if >= 0 this is actually a e-SSA Pi */
|
||||
zend_ssa_pi_constraint constraint; /* e-SSA Pi constraint */
|
||||
int var; /* Original CV, VAR or TMP variable index */
|
||||
int ssa_var; /* SSA variable index */
|
||||
int block; /* current BB index */
|
||||
int visited : 1; /* flag to avoid recursive processing */
|
||||
int has_range_constraint : 1;
|
||||
zend_ssa_phi **use_chains;
|
||||
zend_ssa_phi *sym_use_chain;
|
||||
int *sources; /* Array of SSA IDs that produce this var.
|
||||
As many as this block has
|
||||
predecessors. */
|
||||
};
|
||||
|
||||
typedef struct _zend_ssa_block {
|
||||
zend_ssa_phi *phis;
|
||||
} zend_ssa_block;
|
||||
|
||||
typedef struct _zend_ssa_op {
|
||||
int op1_use;
|
||||
int op2_use;
|
||||
int result_use;
|
||||
int op1_def;
|
||||
int op2_def;
|
||||
int result_def;
|
||||
int op1_use_chain;
|
||||
int op2_use_chain;
|
||||
int res_use_chain;
|
||||
} zend_ssa_op;
|
||||
|
||||
typedef enum _zend_ssa_alias_kind {
|
||||
NO_ALIAS,
|
||||
SYMTABLE_ALIAS,
|
||||
HTTP_RESPONSE_HEADER_ALIAS
|
||||
} zend_ssa_alias_kind;
|
||||
|
||||
typedef enum _zend_ssa_escape_state {
|
||||
ESCAPE_STATE_UNKNOWN,
|
||||
ESCAPE_STATE_NO_ESCAPE,
|
||||
ESCAPE_STATE_FUNCTION_ESCAPE,
|
||||
ESCAPE_STATE_GLOBAL_ESCAPE
|
||||
} zend_ssa_escape_state;
|
||||
|
||||
typedef struct _zend_ssa_var {
|
||||
int var; /* original var number; op.var for CVs and following numbers for VARs and TMP_VARs */
|
||||
int scc; /* strongly connected component */
|
||||
int definition; /* opcode that defines this value */
|
||||
zend_ssa_phi *definition_phi; /* phi that defines this value */
|
||||
int use_chain; /* uses of this value, linked through opN_use_chain */
|
||||
zend_ssa_phi *phi_use_chain; /* uses of this value in Phi, linked through use_chain */
|
||||
zend_ssa_phi *sym_use_chain; /* uses of this value in Pi constraints */
|
||||
unsigned int no_val : 1; /* value doesn't matter (used as op1 in ZEND_ASSIGN) */
|
||||
unsigned int scc_entry : 1;
|
||||
unsigned int alias : 2; /* value may be changed indirectly */
|
||||
unsigned int escape_state : 2;
|
||||
} zend_ssa_var;
|
||||
|
||||
typedef struct _zend_ssa_var_info {
|
||||
uint32_t type; /* inferred type (see zend_inference.h) */
|
||||
zend_ssa_range range;
|
||||
zend_class_entry *ce;
|
||||
unsigned int has_range : 1;
|
||||
unsigned int is_instanceof : 1; /* 0 - class == "ce", 1 - may be child of "ce" */
|
||||
unsigned int recursive : 1;
|
||||
unsigned int use_as_double : 1;
|
||||
unsigned int delayed_fetch_this : 1;
|
||||
unsigned int avoid_refcounting : 1;
|
||||
unsigned int guarded_reference : 1;
|
||||
unsigned int indirect_reference : 1; /* IS_INDIRECT returned by FETCH_DIM_W/FETCH_OBJ_W */
|
||||
} zend_ssa_var_info;
|
||||
|
||||
typedef struct _zend_ssa {
|
||||
zend_cfg cfg; /* control flow graph */
|
||||
int vars_count; /* number of SSA variables */
|
||||
int sccs; /* number of SCCs */
|
||||
zend_ssa_block *blocks; /* array of SSA blocks */
|
||||
zend_ssa_op *ops; /* array of SSA instructions */
|
||||
zend_ssa_var *vars; /* use/def chain of SSA variables */
|
||||
zend_ssa_var_info *var_info;
|
||||
} zend_ssa;
|
||||
|
||||
BEGIN_EXTERN_C()
|
||||
|
||||
ZEND_API int zend_build_ssa(zend_arena **arena, const zend_script *script, const zend_op_array *op_array, uint32_t build_flags, zend_ssa *ssa);
|
||||
ZEND_API int zend_ssa_compute_use_def_chains(zend_arena **arena, const zend_op_array *op_array, zend_ssa *ssa);
|
||||
ZEND_API int zend_ssa_rename_op(const zend_op_array *op_array, const zend_op *opline, uint32_t k, uint32_t build_flags, int ssa_vars_count, zend_ssa_op *ssa_ops, int *var);
|
||||
int zend_ssa_unlink_use_chain(zend_ssa *ssa, int op, int var);
|
||||
|
||||
void zend_ssa_remove_predecessor(zend_ssa *ssa, int from, int to);
|
||||
void zend_ssa_remove_instr(zend_ssa *ssa, zend_op *opline, zend_ssa_op *ssa_op);
|
||||
void zend_ssa_remove_phi(zend_ssa *ssa, zend_ssa_phi *phi);
|
||||
void zend_ssa_remove_uses_of_var(zend_ssa *ssa, int var_num);
|
||||
void zend_ssa_remove_block(zend_op_array *op_array, zend_ssa *ssa, int b);
|
||||
void zend_ssa_rename_var_uses(zend_ssa *ssa, int old_var, int new_var, bool update_types);
|
||||
|
||||
static zend_always_inline void _zend_ssa_remove_def(zend_ssa_var *var)
|
||||
{
|
||||
ZEND_ASSERT(var->definition >= 0);
|
||||
ZEND_ASSERT(var->use_chain < 0);
|
||||
ZEND_ASSERT(!var->phi_use_chain);
|
||||
var->definition = -1;
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_ssa_remove_result_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
|
||||
{
|
||||
zend_ssa_var *var = &ssa->vars[ssa_op->result_def];
|
||||
_zend_ssa_remove_def(var);
|
||||
ssa_op->result_def = -1;
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_ssa_remove_op1_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
|
||||
{
|
||||
zend_ssa_var *var = &ssa->vars[ssa_op->op1_def];
|
||||
_zend_ssa_remove_def(var);
|
||||
ssa_op->op1_def = -1;
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_ssa_remove_op2_def(zend_ssa *ssa, zend_ssa_op *ssa_op)
|
||||
{
|
||||
zend_ssa_var *var = &ssa->vars[ssa_op->op2_def];
|
||||
_zend_ssa_remove_def(var);
|
||||
ssa_op->op2_def = -1;
|
||||
}
|
||||
|
||||
END_EXTERN_C()
|
||||
|
||||
static zend_always_inline int zend_ssa_next_use(const zend_ssa_op *ssa_op, int var, int use)
|
||||
{
|
||||
ssa_op += use;
|
||||
if (ssa_op->op1_use == var) {
|
||||
return ssa_op->op1_use_chain;
|
||||
} else if (ssa_op->op2_use == var) {
|
||||
return ssa_op->op2_use_chain;
|
||||
} else {
|
||||
return ssa_op->res_use_chain;
|
||||
}
|
||||
}
|
||||
|
||||
static zend_always_inline zend_ssa_phi* zend_ssa_next_use_phi(const zend_ssa *ssa, int var, const zend_ssa_phi *p)
|
||||
{
|
||||
if (p->pi >= 0) {
|
||||
return p->use_chains[0];
|
||||
} else {
|
||||
int j;
|
||||
for (j = 0; j < ssa->cfg.blocks[p->block].predecessors_count; j++) {
|
||||
if (p->sources[j] == var) {
|
||||
return p->use_chains[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static zend_always_inline bool zend_ssa_is_no_val_use(const zend_op *opline, const zend_ssa_op *ssa_op, int var)
|
||||
{
|
||||
if (opline->opcode == ZEND_ASSIGN
|
||||
|| opline->opcode == ZEND_UNSET_CV
|
||||
|| opline->opcode == ZEND_BIND_GLOBAL
|
||||
|| opline->opcode == ZEND_BIND_STATIC) {
|
||||
return ssa_op->op1_use == var && ssa_op->op2_use != var;
|
||||
}
|
||||
if (opline->opcode == ZEND_FE_FETCH_R || opline->opcode == ZEND_FE_FETCH_RW) {
|
||||
return ssa_op->op2_use == var && ssa_op->op1_use != var;
|
||||
}
|
||||
if (ssa_op->result_use == var
|
||||
&& opline->opcode != ZEND_ADD_ARRAY_ELEMENT
|
||||
&& opline->opcode != ZEND_ADD_ARRAY_UNPACK) {
|
||||
return ssa_op->op1_use != var && ssa_op->op2_use != var;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zend_always_inline void zend_ssa_rename_defs_of_instr(zend_ssa *ssa, zend_ssa_op *ssa_op) {
|
||||
/* Rename def to use if possible. Mark variable as not defined otherwise. */
|
||||
if (ssa_op->op1_def >= 0) {
|
||||
if (ssa_op->op1_use >= 0) {
|
||||
zend_ssa_rename_var_uses(ssa, ssa_op->op1_def, ssa_op->op1_use, 1);
|
||||
}
|
||||
ssa->vars[ssa_op->op1_def].definition = -1;
|
||||
ssa_op->op1_def = -1;
|
||||
}
|
||||
if (ssa_op->op2_def >= 0) {
|
||||
if (ssa_op->op2_use >= 0) {
|
||||
zend_ssa_rename_var_uses(ssa, ssa_op->op2_def, ssa_op->op2_use, 1);
|
||||
}
|
||||
ssa->vars[ssa_op->op2_def].definition = -1;
|
||||
ssa_op->op2_def = -1;
|
||||
}
|
||||
if (ssa_op->result_def >= 0) {
|
||||
if (ssa_op->result_use >= 0) {
|
||||
zend_ssa_rename_var_uses(ssa, ssa_op->result_def, ssa_op->result_use, 1);
|
||||
}
|
||||
ssa->vars[ssa_op->result_def].definition = -1;
|
||||
ssa_op->result_def = -1;
|
||||
}
|
||||
}
|
||||
|
||||
#define NUM_PHI_SOURCES(phi) \
|
||||
((phi)->pi >= 0 ? 1 : (ssa->cfg.blocks[(phi)->block].predecessors_count))
|
||||
|
||||
/* FOREACH_USE and FOREACH_PHI_USE explicitly support "continue"
|
||||
* and changing the use chain of the current element */
|
||||
#define FOREACH_USE(var, use) do { \
|
||||
int _var_num = (var) - ssa->vars, next; \
|
||||
for (use = (var)->use_chain; use >= 0; use = next) { \
|
||||
next = zend_ssa_next_use(ssa->ops, _var_num, use);
|
||||
#define FOREACH_USE_END() \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FOREACH_PHI_USE(var, phi) do { \
|
||||
int _var_num = (var) - ssa->vars; \
|
||||
zend_ssa_phi *next_phi; \
|
||||
for (phi = (var)->phi_use_chain; phi; phi = next_phi) { \
|
||||
next_phi = zend_ssa_next_use_phi(ssa, _var_num, phi);
|
||||
#define FOREACH_PHI_USE_END() \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FOREACH_PHI_SOURCE(phi, source) do { \
|
||||
zend_ssa_phi *_phi = (phi); \
|
||||
int _i, _end = NUM_PHI_SOURCES(phi); \
|
||||
for (_i = 0; _i < _end; _i++) { \
|
||||
ZEND_ASSERT(_phi->sources[_i] >= 0); \
|
||||
source = _phi->sources[_i];
|
||||
#define FOREACH_PHI_SOURCE_END() \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FOREACH_PHI(phi) do { \
|
||||
int _i; \
|
||||
for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
|
||||
phi = ssa->blocks[_i].phis; \
|
||||
for (; phi; phi = phi->next) {
|
||||
#define FOREACH_PHI_END() \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define FOREACH_BLOCK(block) do { \
|
||||
int _i; \
|
||||
for (_i = 0; _i < ssa->cfg.blocks_count; _i++) { \
|
||||
(block) = &ssa->cfg.blocks[_i]; \
|
||||
if (!((block)->flags & ZEND_BB_REACHABLE)) { \
|
||||
continue; \
|
||||
}
|
||||
#define FOREACH_BLOCK_END() \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Does not support "break" */
|
||||
#define FOREACH_INSTR_NUM(i) do { \
|
||||
zend_basic_block *_block; \
|
||||
FOREACH_BLOCK(_block) { \
|
||||
uint32_t _end = _block->start + _block->len; \
|
||||
for ((i) = _block->start; (i) < _end; (i)++) {
|
||||
#define FOREACH_INSTR_NUM_END() \
|
||||
} \
|
||||
} FOREACH_BLOCK_END(); \
|
||||
} while (0)
|
||||
|
||||
#endif /* ZEND_SSA_H */
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 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: |
|
||||
| https://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: Andy Wingo <wingo@igalia.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef _ZEND_WORKLIST_H_
|
||||
#define _ZEND_WORKLIST_H_
|
||||
|
||||
#include "zend_arena.h"
|
||||
#include "zend_bitset.h"
|
||||
|
||||
typedef struct _zend_worklist_stack {
|
||||
int *buf;
|
||||
int len;
|
||||
int capacity;
|
||||
} zend_worklist_stack;
|
||||
|
||||
#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
|
||||
(s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
|
||||
(s)->len = 0; \
|
||||
(s)->capacity = _len; \
|
||||
} while (0)
|
||||
|
||||
#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
|
||||
free_alloca((s)->buf, use_heap)
|
||||
|
||||
static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
|
||||
{
|
||||
ZEND_ASSERT(len >= 0);
|
||||
|
||||
stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
|
||||
stack->len = 0;
|
||||
stack->capacity = len;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
|
||||
{
|
||||
ZEND_ASSERT(stack->len < stack->capacity);
|
||||
stack->buf[stack->len++] = i;
|
||||
}
|
||||
|
||||
static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
|
||||
{
|
||||
ZEND_ASSERT(stack->len);
|
||||
return stack->buf[stack->len - 1];
|
||||
}
|
||||
|
||||
static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
|
||||
{
|
||||
ZEND_ASSERT(stack->len);
|
||||
return stack->buf[--stack->len];
|
||||
}
|
||||
|
||||
typedef struct _zend_worklist {
|
||||
zend_bitset visited;
|
||||
zend_worklist_stack stack;
|
||||
} zend_worklist;
|
||||
|
||||
#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
|
||||
(w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
|
||||
(w)->stack.len = 0; \
|
||||
(w)->stack.capacity = _len; \
|
||||
(w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
|
||||
memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
|
||||
} while (0)
|
||||
|
||||
#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
|
||||
free_alloca((w)->stack.buf, use_heap)
|
||||
|
||||
static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
|
||||
{
|
||||
ZEND_ASSERT(len >= 0);
|
||||
worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
|
||||
return zend_worklist_stack_prepare(arena, &worklist->stack, len);
|
||||
}
|
||||
|
||||
static inline int zend_worklist_len(zend_worklist *worklist)
|
||||
{
|
||||
return worklist->stack.len;
|
||||
}
|
||||
|
||||
static inline int zend_worklist_push(zend_worklist *worklist, int i)
|
||||
{
|
||||
ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
|
||||
|
||||
if (zend_bitset_in(worklist->visited, i)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
zend_bitset_incl(worklist->visited, i);
|
||||
zend_worklist_stack_push(&worklist->stack, i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int zend_worklist_peek(zend_worklist *worklist)
|
||||
{
|
||||
return zend_worklist_stack_peek(&worklist->stack);
|
||||
}
|
||||
|
||||
static inline int zend_worklist_pop(zend_worklist *worklist)
|
||||
{
|
||||
/* Does not clear visited flag */
|
||||
return zend_worklist_stack_pop(&worklist->stack);
|
||||
}
|
||||
|
||||
#endif /* _ZEND_WORKLIST_H_ */
|
||||
@@ -0,0 +1,43 @@
|
||||
Zend Memory Manager
|
||||
===================
|
||||
|
||||
General:
|
||||
--------
|
||||
|
||||
The goal of the new memory manager (available since PHP 5.2) is to reduce memory
|
||||
allocation overhead and speedup memory management.
|
||||
|
||||
The new manager's "configure" has no "--disable-zend-memory-manager" option,
|
||||
but it has "--enable-malloc-mm" instead. It is enabled by default in DEBUG
|
||||
build and disabled by default in RELEASE build. When enabled it allows selecting
|
||||
between malloc and emalloc at runtime so you can use internal and external memory
|
||||
debuggers without recompilation.
|
||||
|
||||
Debugging:
|
||||
----------
|
||||
|
||||
Normal:
|
||||
|
||||
$ sapi/cli/php -r 'leak();'
|
||||
|
||||
Zend MM disabled:
|
||||
|
||||
$ USE_ZEND_ALLOC=0 valgrind --leak-check=full sapi/cli/php -r 'leak();'
|
||||
|
||||
Shared extensions:
|
||||
------------------
|
||||
|
||||
Since PHP 5.3.11 it is possible to prevent shared extensions from unloading so
|
||||
that valgrind can correctly track the memory leaks in shared extensions. For
|
||||
this there is the ZEND_DONT_UNLOAD_MODULES environment variable. If set, then
|
||||
DL_UNLOAD() is skipped during the shutdown of shared extensions.
|
||||
|
||||
|
||||
Tweaking:
|
||||
---------
|
||||
|
||||
The Zend MM can be tweaked using ZEND_MM_MEM_TYPE and ZEND_MM_SEG_SIZE environment
|
||||
variables. Default values are "malloc" and "256K". Dependent on target system you
|
||||
can also use "mmap_anon", "mmap_zero" and "win32" storage managers.
|
||||
|
||||
$ ZEND_MM_MEM_TYPE=mmap_anon ZEND_MM_SEG_SIZE=1M sapi/cli/php ..etc.
|
||||
Loaded 100 of 22724 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user