feature #14159 [Debug] Add symfony_debug_backtrace() and use it when dealing with fatal errors (jpauli, nicolas-grekas)
This PR was merged into the 2.7 branch. Discussion ---------- [Debug] Add symfony_debug_backtrace() and use it when dealing with fatal errors | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This takes a subset of #12159 that would be great in 2.7, and uses it to gather back traces of fatal errors when possible. Commits -------5cbd9fe
[Debug] Updated CHANGELOGbeb88f7
[Debug] Use symfony_debug_backtrace() in FatalErrorException when available6487b6e
[Debug] Add debug extension to the test suiteeacdc62
[Debug] Add symfony_debug_backtrace() that works with fatal errors
This commit is contained in:
commit
d0676afa72
|
@ -31,6 +31,7 @@ before_install:
|
||||||
- if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
|
- if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
|
||||||
- if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
|
- if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;
|
||||||
- if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then (pecl install -f memcached-2.1.0 && echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini) || echo "Let's continue without memcache extension"; fi;
|
- if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then (pecl install -f memcached-2.1.0 && echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini) || echo "Let's continue without memcache extension"; fi;
|
||||||
|
- if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo "extension = $(pwd)/modules/symfony_debug.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini); fi;
|
||||||
- if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then php -i; fi;
|
- if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then php -i; fi;
|
||||||
- sudo locale-gen fr_FR.UTF-8 && sudo update-locale
|
- sudo locale-gen fr_FR.UTF-8 && sudo update-locale
|
||||||
# Set the COMPOSER_ROOT_VERSION to the right version according to the branch being built
|
# Set the COMPOSER_ROOT_VERSION to the right version according to the branch being built
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
2.7.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* added deprecations checking for parent interfaces/classes to DebugClassLoader
|
||||||
|
* added ZTS support to symfony_debug extension
|
||||||
|
* added symfony_debug_backtrace() to symfony_debug extension
|
||||||
|
to track the backtrace of fatal errors
|
||||||
|
|
||||||
2.6.0
|
2.6.0
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,11 @@ class FatalErrorException extends LegacyFatalErrorException
|
||||||
|
|
||||||
unset($frame);
|
unset($frame);
|
||||||
$trace = array_reverse($trace);
|
$trace = array_reverse($trace);
|
||||||
|
} elseif (function_exists('symfony_debug_backtrace')) {
|
||||||
|
$trace = symfony_debug_backtrace();
|
||||||
|
if (0 < $traceOffset) {
|
||||||
|
array_splice($trace, 0, $traceOffset);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$trace = array();
|
$trace = array();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
Symfony Debug Extension
|
||||||
|
=======================
|
||||||
|
|
||||||
|
This extension publishes several functions to help building powerful debugging tools.
|
||||||
|
|
||||||
|
symfony_zval_info()
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP,
|
||||||
|
- does work with references, preventing memory copying.
|
||||||
|
|
||||||
|
Its behavior is about the same as:
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function symfony_zval_info($key, $array, $options = 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
// $options is currently not used, but could be in future version.
|
||||||
|
|
||||||
|
if (!array_key_exists($key, $array)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info = array(
|
||||||
|
'type' => gettype($array[$key]),
|
||||||
|
'zval_hash' => /* hashed memory address of $array[$key] */,
|
||||||
|
'zval_refcount' => /* internal zval refcount of $array[$key] */,
|
||||||
|
'zval_isref' => /* is_ref status of $array[$key] */,
|
||||||
|
);
|
||||||
|
|
||||||
|
switch ($info['type']) {
|
||||||
|
case 'object':
|
||||||
|
$info += array(
|
||||||
|
'object_class' => get_class($array[$key]),
|
||||||
|
'object_refcount' => /* internal object refcount of $array[$key] */,
|
||||||
|
'object_hash' => spl_object_hash($array[$key]),
|
||||||
|
'object_handle' => /* internal object handle $array[$key] */,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'resource':
|
||||||
|
$info += array(
|
||||||
|
'resource_handle' => (int) $array[$key],
|
||||||
|
'resource_type' => get_resource_type($array[$key]),
|
||||||
|
'resource_refcount' => /* internal resource refcount of $array[$key] */,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'array':
|
||||||
|
$info += array(
|
||||||
|
'array_count' => count($array[$key]),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'string':
|
||||||
|
$info += array(
|
||||||
|
'strlen' => strlen($array[$key]),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
symfony_debug_backtrace()
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
This function works like debug_backtrace(), except that it can fetch the full backtrace in case of fatal errors:
|
||||||
|
|
||||||
|
```php
|
||||||
|
function foo() { fatal(); }
|
||||||
|
function bar() { foo(); }
|
||||||
|
|
||||||
|
function sd() { var_dump(symfony_debug_backtrace()); }
|
||||||
|
|
||||||
|
register_shutdown_function('sd');
|
||||||
|
|
||||||
|
bar();
|
||||||
|
|
||||||
|
/* Will output
|
||||||
|
Fatal error: Call to undefined function fatal() in foo.php on line 42
|
||||||
|
array(3) {
|
||||||
|
[0]=>
|
||||||
|
array(2) {
|
||||||
|
["function"]=>
|
||||||
|
string(2) "sd"
|
||||||
|
["args"]=>
|
||||||
|
array(0) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[1]=>
|
||||||
|
array(4) {
|
||||||
|
["file"]=>
|
||||||
|
string(7) "foo.php"
|
||||||
|
["line"]=>
|
||||||
|
int(1)
|
||||||
|
["function"]=>
|
||||||
|
string(3) "foo"
|
||||||
|
["args"]=>
|
||||||
|
array(0) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[2]=>
|
||||||
|
array(4) {
|
||||||
|
["file"]=>
|
||||||
|
string(102) "foo.php"
|
||||||
|
["line"]=>
|
||||||
|
int(2)
|
||||||
|
["function"]=>
|
||||||
|
string(3) "bar"
|
||||||
|
["args"]=>
|
||||||
|
array(0) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
The extension is compatible with ZTS mode, and should be supported by PHP5.3, 5.4, 5.5 and 5.6.
|
||||||
|
To enable the extension from source, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
phpize
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
```
|
|
@ -1,72 +0,0 @@
|
||||||
Symfony Debug Extension
|
|
||||||
=======================
|
|
||||||
|
|
||||||
This extension adds a ``symfony_zval_info($key, $array, $options = 0)`` function that:
|
|
||||||
|
|
||||||
- exposes zval_hash/refcounts, allowing e.g. efficient exploration of arbitrary structures in PHP,
|
|
||||||
- does work with references, preventing memory copying.
|
|
||||||
|
|
||||||
Its behavior is about the same as:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
function symfony_zval_info($key, $array, $options = 0)
|
|
||||||
{
|
|
||||||
// $options is currently not used, but could be in future version.
|
|
||||||
|
|
||||||
if (!array_key_exists($key, $array)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$info = array(
|
|
||||||
'type' => gettype($array[$key]),
|
|
||||||
'zval_hash' => /* hashed memory address of $array[$key] */,
|
|
||||||
'zval_refcount' => /* internal zval refcount of $array[$key] */,
|
|
||||||
'zval_isref' => /* is_ref status of $array[$key] */,
|
|
||||||
);
|
|
||||||
|
|
||||||
switch ($info['type']) {
|
|
||||||
case 'object':
|
|
||||||
$info += array(
|
|
||||||
'object_class' => get_class($array[$key]),
|
|
||||||
'object_refcount' => /* internal object refcount of $array[$key] */,
|
|
||||||
'object_hash' => spl_object_hash($array[$key]),
|
|
||||||
'object_handle' => /* internal object handle $array[$key] */,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'resource':
|
|
||||||
$info += array(
|
|
||||||
'resource_handle' => (int) $array[$key],
|
|
||||||
'resource_type' => get_resource_type($array[$key]),
|
|
||||||
'resource_refcount' => /* internal resource refcount of $array[$key] */,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'array':
|
|
||||||
$info += array(
|
|
||||||
'array_count' => count($array[$key]),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'string':
|
|
||||||
$info += array(
|
|
||||||
'strlen' => strlen($array[$key]),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $info;
|
|
||||||
}
|
|
||||||
|
|
||||||
To enable the extension from source, run:
|
|
||||||
|
|
||||||
.. code-block:: sh
|
|
||||||
|
|
||||||
phpize
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
sudo make install
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
extern zend_module_entry symfony_debug_module_entry;
|
extern zend_module_entry symfony_debug_module_entry;
|
||||||
#define phpext_symfony_debug_ptr &symfony_debug_module_entry
|
#define phpext_symfony_debug_ptr &symfony_debug_module_entry
|
||||||
|
|
||||||
#define PHP_SYMFONY_DEBUG_VERSION "1.0"
|
#define PHP_SYMFONY_DEBUG_VERSION "2.7"
|
||||||
|
|
||||||
#ifdef PHP_WIN32
|
#ifdef PHP_WIN32
|
||||||
# define PHP_SYMFONY_DEBUG_API __declspec(dllexport)
|
# define PHP_SYMFONY_DEBUG_API __declspec(dllexport)
|
||||||
|
@ -29,6 +29,8 @@ extern zend_module_entry symfony_debug_module_entry;
|
||||||
|
|
||||||
ZEND_BEGIN_MODULE_GLOBALS(symfony_debug)
|
ZEND_BEGIN_MODULE_GLOBALS(symfony_debug)
|
||||||
intptr_t req_rand_init;
|
intptr_t req_rand_init;
|
||||||
|
void (*old_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args);
|
||||||
|
zval *debug_bt;
|
||||||
ZEND_END_MODULE_GLOBALS(symfony_debug)
|
ZEND_END_MODULE_GLOBALS(symfony_debug)
|
||||||
|
|
||||||
PHP_MINIT_FUNCTION(symfony_debug);
|
PHP_MINIT_FUNCTION(symfony_debug);
|
||||||
|
@ -40,11 +42,14 @@ PHP_GINIT_FUNCTION(symfony_debug);
|
||||||
PHP_GSHUTDOWN_FUNCTION(symfony_debug);
|
PHP_GSHUTDOWN_FUNCTION(symfony_debug);
|
||||||
|
|
||||||
PHP_FUNCTION(symfony_zval_info);
|
PHP_FUNCTION(symfony_zval_info);
|
||||||
|
PHP_FUNCTION(symfony_debug_backtrace);
|
||||||
|
|
||||||
static char *_symfony_debug_memory_address_hash(void *);
|
static char *_symfony_debug_memory_address_hash(void * TSRMLS_DC);
|
||||||
static const char *_symfony_debug_zval_type(zval *);
|
static const char *_symfony_debug_zval_type(zval *);
|
||||||
static const char* _symfony_debug_get_resource_type(long);
|
static const char* _symfony_debug_get_resource_type(long TSRMLS_DC);
|
||||||
static int _symfony_debug_get_resource_refcount(long);
|
static int _symfony_debug_get_resource_refcount(long TSRMLS_DC);
|
||||||
|
|
||||||
|
void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args);
|
||||||
|
|
||||||
#ifdef ZTS
|
#ifdef ZTS
|
||||||
#define SYMFONY_DEBUG_G(v) TSRMG(symfony_debug_globals_id, zend_symfony_debug_globals *, v)
|
#define SYMFONY_DEBUG_G(v) TSRMG(symfony_debug_globals_id, zend_symfony_debug_globals *, v)
|
||||||
|
|
|
@ -12,6 +12,9 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "php.h"
|
#include "php.h"
|
||||||
|
#ifdef ZTS
|
||||||
|
#include "TSRM.h"
|
||||||
|
#endif
|
||||||
#include "php_ini.h"
|
#include "php_ini.h"
|
||||||
#include "ext/standard/info.h"
|
#include "ext/standard/info.h"
|
||||||
#include "php_symfony_debug.h"
|
#include "php_symfony_debug.h"
|
||||||
|
@ -19,6 +22,13 @@
|
||||||
#include "ext/standard/php_lcg.h"
|
#include "ext/standard/php_lcg.h"
|
||||||
#include "ext/spl/php_spl.h"
|
#include "ext/spl/php_spl.h"
|
||||||
#include "Zend/zend_gc.h"
|
#include "Zend/zend_gc.h"
|
||||||
|
#include "Zend/zend_builtin_functions.h"
|
||||||
|
#include "Zend/zend_extensions.h" /* for ZEND_EXTENSION_API_NO */
|
||||||
|
#include "ext/standard/php_array.h"
|
||||||
|
#include "Zend/zend_interfaces.h"
|
||||||
|
#include "SAPI.h"
|
||||||
|
|
||||||
|
#define IS_PHP_53 ZEND_EXTENSION_API_NO == 220090626
|
||||||
|
|
||||||
ZEND_DECLARE_MODULE_GLOBALS(symfony_debug)
|
ZEND_DECLARE_MODULE_GLOBALS(symfony_debug)
|
||||||
|
|
||||||
|
@ -30,9 +40,28 @@ ZEND_END_ARG_INFO()
|
||||||
|
|
||||||
const zend_function_entry symfony_debug_functions[] = {
|
const zend_function_entry symfony_debug_functions[] = {
|
||||||
PHP_FE(symfony_zval_info, symfony_zval_arginfo)
|
PHP_FE(symfony_zval_info, symfony_zval_arginfo)
|
||||||
|
PHP_FE(symfony_debug_backtrace, NULL)
|
||||||
PHP_FE_END
|
PHP_FE_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PHP_FUNCTION(symfony_debug_backtrace)
|
||||||
|
{
|
||||||
|
if (zend_parse_parameters_none() == FAILURE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if IS_PHP_53
|
||||||
|
zend_fetch_debug_backtrace(return_value, 1, 0 TSRMLS_CC);
|
||||||
|
#else
|
||||||
|
zend_fetch_debug_backtrace(return_value, 1, 0, 0 TSRMLS_CC);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!SYMFONY_DEBUG_G(debug_bt)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
php_array_merge(Z_ARRVAL_P(return_value), Z_ARRVAL_P(SYMFONY_DEBUG_G(debug_bt)), 0 TSRMLS_CC);
|
||||||
|
}
|
||||||
|
|
||||||
PHP_FUNCTION(symfony_zval_info)
|
PHP_FUNCTION(symfony_zval_info)
|
||||||
{
|
{
|
||||||
zval *key = NULL, *arg = NULL;
|
zval *key = NULL, *arg = NULL;
|
||||||
|
@ -40,7 +69,7 @@ PHP_FUNCTION(symfony_zval_info)
|
||||||
HashTable *array = NULL;
|
HashTable *array = NULL;
|
||||||
long options = 0;
|
long options = 0;
|
||||||
|
|
||||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zh|l", &key, &array, &options) == FAILURE) {
|
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zh|l", &key, &array, &options) == FAILURE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,13 +91,14 @@ PHP_FUNCTION(symfony_zval_info)
|
||||||
array_init(return_value);
|
array_init(return_value);
|
||||||
|
|
||||||
add_assoc_string(return_value, "type", (char *)_symfony_debug_zval_type(arg), 1);
|
add_assoc_string(return_value, "type", (char *)_symfony_debug_zval_type(arg), 1);
|
||||||
add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg), 16, 1);
|
add_assoc_stringl(return_value, "zval_hash", _symfony_debug_memory_address_hash((void *)arg TSRMLS_CC), 16, 0);
|
||||||
add_assoc_long(return_value, "zval_refcount", Z_REFCOUNT_P(arg));
|
add_assoc_long(return_value, "zval_refcount", Z_REFCOUNT_P(arg));
|
||||||
add_assoc_bool(return_value, "zval_isref", (zend_bool)Z_ISREF_P(arg));
|
add_assoc_bool(return_value, "zval_isref", (zend_bool)Z_ISREF_P(arg));
|
||||||
|
|
||||||
if (Z_TYPE_P(arg) == IS_OBJECT) {
|
if (Z_TYPE_P(arg) == IS_OBJECT) {
|
||||||
static char hash[33] = {0};
|
char hash[33] = {0};
|
||||||
php_spl_object_hash(arg, (char *)hash);
|
|
||||||
|
php_spl_object_hash(arg, (char *)hash TSRMLS_CC);
|
||||||
add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1);
|
add_assoc_stringl(return_value, "object_class", (char *)Z_OBJCE_P(arg)->name, Z_OBJCE_P(arg)->name_length, 1);
|
||||||
add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount);
|
add_assoc_long(return_value, "object_refcount", EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(arg)].bucket.obj.refcount);
|
||||||
add_assoc_string(return_value, "object_hash", hash, 1);
|
add_assoc_string(return_value, "object_hash", hash, 1);
|
||||||
|
@ -77,17 +107,41 @@ PHP_FUNCTION(symfony_zval_info)
|
||||||
add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg)));
|
add_assoc_long(return_value, "array_count", zend_hash_num_elements(Z_ARRVAL_P(arg)));
|
||||||
} else if(Z_TYPE_P(arg) == IS_RESOURCE) {
|
} else if(Z_TYPE_P(arg) == IS_RESOURCE) {
|
||||||
add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg));
|
add_assoc_long(return_value, "resource_handle", Z_LVAL_P(arg));
|
||||||
add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg)), 1);
|
add_assoc_string(return_value, "resource_type", (char *)_symfony_debug_get_resource_type(Z_LVAL_P(arg) TSRMLS_CC), 1);
|
||||||
add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg)));
|
add_assoc_long(return_value, "resource_refcount", _symfony_debug_get_resource_refcount(Z_LVAL_P(arg) TSRMLS_CC));
|
||||||
} else if (Z_TYPE_P(arg) == IS_STRING) {
|
} else if (Z_TYPE_P(arg) == IS_STRING) {
|
||||||
add_assoc_long(return_value, "strlen", Z_STRLEN_P(arg));
|
add_assoc_long(return_value, "strlen", Z_STRLEN_P(arg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* _symfony_debug_get_resource_type(long rsid)
|
void symfony_debug_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args)
|
||||||
|
{
|
||||||
|
TSRMLS_FETCH();
|
||||||
|
zval *retval;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case E_ERROR:
|
||||||
|
case E_PARSE:
|
||||||
|
case E_CORE_ERROR:
|
||||||
|
case E_CORE_WARNING:
|
||||||
|
case E_COMPILE_ERROR:
|
||||||
|
case E_COMPILE_WARNING:
|
||||||
|
ALLOC_INIT_ZVAL(retval);
|
||||||
|
#if IS_PHP_53
|
||||||
|
zend_fetch_debug_backtrace(retval, 1, 0 TSRMLS_CC);
|
||||||
|
#else
|
||||||
|
zend_fetch_debug_backtrace(retval, 1, 0, 0 TSRMLS_CC);
|
||||||
|
#endif
|
||||||
|
SYMFONY_DEBUG_G(debug_bt) = retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYMFONY_DEBUG_G(old_error_cb)(type, error_filename, error_lineno, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* _symfony_debug_get_resource_type(long rsid TSRMLS_DC)
|
||||||
{
|
{
|
||||||
const char *res_type;
|
const char *res_type;
|
||||||
res_type = zend_rsrc_list_get_rsrc_type(rsid);
|
res_type = zend_rsrc_list_get_rsrc_type(rsid TSRMLS_CC);
|
||||||
|
|
||||||
if (!res_type) {
|
if (!res_type) {
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
|
@ -96,7 +150,7 @@ static const char* _symfony_debug_get_resource_type(long rsid)
|
||||||
return res_type;
|
return res_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _symfony_debug_get_resource_refcount(long rsid)
|
static int _symfony_debug_get_resource_refcount(long rsid TSRMLS_DC)
|
||||||
{
|
{
|
||||||
zend_rsrc_list_entry *le;
|
zend_rsrc_list_entry *le;
|
||||||
|
|
||||||
|
@ -107,21 +161,21 @@ static int _symfony_debug_get_resource_refcount(long rsid)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *_symfony_debug_memory_address_hash(void *address)
|
static char *_symfony_debug_memory_address_hash(void *address TSRMLS_DC)
|
||||||
{
|
{
|
||||||
static char result[17] = {0};
|
char *result = NULL;
|
||||||
intptr_t address_rand;
|
intptr_t address_rand;
|
||||||
|
|
||||||
if (!SYMFONY_DEBUG_G(req_rand_init)) {
|
if (!SYMFONY_DEBUG_G(req_rand_init)) {
|
||||||
if (!BG(mt_rand_is_seeded)) {
|
if (!BG(mt_rand_is_seeded)) {
|
||||||
php_mt_srand(GENERATE_SEED() TSRMLS_CC);
|
php_mt_srand(GENERATE_SEED() TSRMLS_CC);
|
||||||
}
|
}
|
||||||
SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand();
|
SYMFONY_DEBUG_G(req_rand_init) = (intptr_t)php_mt_rand(TSRMLS_C);
|
||||||
}
|
}
|
||||||
|
|
||||||
address_rand = (intptr_t)address ^ SYMFONY_DEBUG_G(req_rand_init);
|
address_rand = (intptr_t)address ^ SYMFONY_DEBUG_G(req_rand_init);
|
||||||
|
|
||||||
snprintf(result, 17, "%016zx", address_rand);
|
spprintf(&result, 17, "%016zx", address_rand);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -187,7 +241,7 @@ ZEND_GET_MODULE(symfony_debug)
|
||||||
|
|
||||||
PHP_GINIT_FUNCTION(symfony_debug)
|
PHP_GINIT_FUNCTION(symfony_debug)
|
||||||
{
|
{
|
||||||
symfony_debug_globals->req_rand_init = 0;
|
memset(symfony_debug_globals, 0 , sizeof(*symfony_debug_globals));
|
||||||
}
|
}
|
||||||
|
|
||||||
PHP_GSHUTDOWN_FUNCTION(symfony_debug)
|
PHP_GSHUTDOWN_FUNCTION(symfony_debug)
|
||||||
|
@ -197,11 +251,16 @@ PHP_GSHUTDOWN_FUNCTION(symfony_debug)
|
||||||
|
|
||||||
PHP_MINIT_FUNCTION(symfony_debug)
|
PHP_MINIT_FUNCTION(symfony_debug)
|
||||||
{
|
{
|
||||||
|
SYMFONY_DEBUG_G(old_error_cb) = zend_error_cb;
|
||||||
|
zend_error_cb = symfony_debug_error_cb;
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
PHP_MSHUTDOWN_FUNCTION(symfony_debug)
|
PHP_MSHUTDOWN_FUNCTION(symfony_debug)
|
||||||
{
|
{
|
||||||
|
zend_error_cb = SYMFONY_DEBUG_G(old_error_cb);
|
||||||
|
|
||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ Test symfony_zval_info API
|
||||||
--SKIPIF--
|
--SKIPIF--
|
||||||
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
|
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
|
||||||
--FILE--
|
--FILE--
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$int = 42;
|
$int = 42;
|
||||||
$float = 42.42;
|
$float = 42.42;
|
||||||
|
@ -88,7 +88,7 @@ array(8) {
|
||||||
["object_hash"]=>
|
["object_hash"]=>
|
||||||
string(32) "%s"
|
string(32) "%s"
|
||||||
["object_handle"]=>
|
["object_handle"]=>
|
||||||
int(1)
|
int(%d)
|
||||||
}
|
}
|
||||||
array(5) {
|
array(5) {
|
||||||
["type"]=>
|
["type"]=>
|
||||||
|
@ -112,7 +112,7 @@ array(7) {
|
||||||
["zval_isref"]=>
|
["zval_isref"]=>
|
||||||
bool(false)
|
bool(false)
|
||||||
["resource_handle"]=>
|
["resource_handle"]=>
|
||||||
int(4)
|
int(%d)
|
||||||
["resource_type"]=>
|
["resource_type"]=>
|
||||||
string(6) "stream"
|
string(6) "stream"
|
||||||
["resource_refcount"]=>
|
["resource_refcount"]=>
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
--TEST--
|
||||||
|
Test symfony_debug_backtrace in case of fatal error
|
||||||
|
--SKIPIF--
|
||||||
|
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function bar()
|
||||||
|
{
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function foo()
|
||||||
|
{
|
||||||
|
notexist();
|
||||||
|
}
|
||||||
|
|
||||||
|
function bt()
|
||||||
|
{
|
||||||
|
print_r(symfony_debug_backtrace());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
register_shutdown_function('bt');
|
||||||
|
|
||||||
|
bar();
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Call to undefined function notexist() in %s on line %d
|
||||||
|
Array
|
||||||
|
(
|
||||||
|
[0] => Array
|
||||||
|
(
|
||||||
|
[function] => bt
|
||||||
|
[args] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[1] => Array
|
||||||
|
(
|
||||||
|
[file] => %s
|
||||||
|
[line] => %d
|
||||||
|
[function] => foo
|
||||||
|
[args] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[2] => Array
|
||||||
|
(
|
||||||
|
[file] => %s
|
||||||
|
[line] => %d
|
||||||
|
[function] => bar
|
||||||
|
[args] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
|
@ -0,0 +1,47 @@
|
||||||
|
--TEST--
|
||||||
|
Test symfony_debug_backtrace in case of non fatal error
|
||||||
|
--SKIPIF--
|
||||||
|
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function bar()
|
||||||
|
{
|
||||||
|
bt();
|
||||||
|
}
|
||||||
|
|
||||||
|
function bt()
|
||||||
|
{
|
||||||
|
print_r(symfony_debug_backtrace());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bar();
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Array
|
||||||
|
(
|
||||||
|
[0] => Array
|
||||||
|
(
|
||||||
|
[file] => %s
|
||||||
|
[line] => %d
|
||||||
|
[function] => bt
|
||||||
|
[args] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[1] => Array
|
||||||
|
(
|
||||||
|
[file] => %s
|
||||||
|
[line] => %d
|
||||||
|
[function] => bar
|
||||||
|
[args] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
|
@ -0,0 +1,85 @@
|
||||||
|
--TEST--
|
||||||
|
Test ErrorHandler in case of fatal error
|
||||||
|
--SKIPIF--
|
||||||
|
<?php if (!extension_loaded("symfony_debug")) print "skip"; ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Psr\Log;
|
||||||
|
|
||||||
|
class LogLevel
|
||||||
|
{
|
||||||
|
const EMERGENCY = 'emergency';
|
||||||
|
const ALERT = 'alert';
|
||||||
|
const CRITICAL = 'critical';
|
||||||
|
const ERROR = 'error';
|
||||||
|
const WARNING = 'warning';
|
||||||
|
const NOTICE = 'notice';
|
||||||
|
const INFO = 'info';
|
||||||
|
const DEBUG = 'debug';
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Symfony\Component\Debug;
|
||||||
|
|
||||||
|
$dir = __DIR__.'/../../../';
|
||||||
|
require $dir.'ErrorHandler.php';
|
||||||
|
require $dir.'Exception/FatalErrorException.php';
|
||||||
|
require $dir.'Exception/UndefinedFunctionException.php';
|
||||||
|
require $dir.'FatalErrorHandler/FatalErrorHandlerInterface.php';
|
||||||
|
require $dir.'FatalErrorHandler/ClassNotFoundFatalErrorHandler.php';
|
||||||
|
require $dir.'FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php';
|
||||||
|
require $dir.'FatalErrorHandler/UndefinedMethodFatalErrorHandler.php';
|
||||||
|
|
||||||
|
function bar()
|
||||||
|
{
|
||||||
|
foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
function foo()
|
||||||
|
{
|
||||||
|
notexist();
|
||||||
|
}
|
||||||
|
|
||||||
|
$handler = ErrorHandler::register();
|
||||||
|
$handler->setExceptionHandler('print_r');
|
||||||
|
|
||||||
|
if (function_exists('xdebug_disable')) {
|
||||||
|
xdebug_disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bar();
|
||||||
|
?>
|
||||||
|
--EXPECTF--
|
||||||
|
Fatal error: Call to undefined function Symfony\Component\Debug\notexist() in %s on line %d
|
||||||
|
Symfony\Component\Debug\Exception\UndefinedFunctionException Object
|
||||||
|
(
|
||||||
|
[message:protected] => Attempted to call function "notexist" from namespace "Symfony\Component\Debug".
|
||||||
|
[string:Exception:private] =>
|
||||||
|
[code:protected] => 0
|
||||||
|
[file:protected] => -
|
||||||
|
[line:protected] => %d
|
||||||
|
[trace:Exception:private] => Array
|
||||||
|
(
|
||||||
|
[0] => Array
|
||||||
|
(
|
||||||
|
%A [function] => Symfony\Component\Debug\foo
|
||||||
|
%A [args] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
[1] => Array
|
||||||
|
(
|
||||||
|
%A [function] => Symfony\Component\Debug\bar
|
||||||
|
%A [args] => Array
|
||||||
|
(
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
%A
|
||||||
|
)
|
||||||
|
|
||||||
|
[previous:Exception:private] =>
|
||||||
|
[severity:protected] => 1
|
||||||
|
)
|
|
@ -14,6 +14,9 @@
|
||||||
<testsuite name="Symfony Debug Component Test Suite">
|
<testsuite name="Symfony Debug Component Test Suite">
|
||||||
<directory>./Tests/</directory>
|
<directory>./Tests/</directory>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
<testsuite name="Symfony Debug Extension Test Suite">
|
||||||
|
<directory suffix=".phpt">./Resources/ext/tests/</directory>
|
||||||
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
|
|
||||||
<filter>
|
<filter>
|
||||||
|
|
Reference in New Issue