diff --git a/Readme.md b/Readme.md index ce8e3e9..b874b71 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,8 @@ PHP Error | Improve Error Reporting for PHP =========================================== +**THIS IS NO LOBGER MAINTAINED, Please leave me alone.** + PHP errors are not good enough for development, it's as simple as that. This aims to solve this.  @@ -15,7 +17,19 @@ If the server errors during an ajax request, then the request is paused, and the This requires no changes to your JavaScript, and works with existing JS libraries such as jQuery. -Check out the [project homepage](http://phperror.net) for a live demo. +Do not use on a live site! +-------------------------- + +To help make development easier, this __deliberately__ makes your code unsafe. +External requests are allowed to change your code, it shows more about your site, +gives you more info, and makes trivial errors fatal. +All of that is awesome if you want to fix bugs in less time, +but in production, it is totally unsafe. + +**seriously, only use this for development!** + +In case you forget, +you can disable this in production using the 'php_error.force_disabled' php.ini option (see below). Features -------- @@ -32,7 +46,7 @@ Features Getting Started --------------- - * [Download](http://phperror.net/download/php_error.php), it's just one file. + * Download, it's just one file. * Place it in your project. * import php_error.php * call \php_error\reportErrors() @@ -55,16 +69,6 @@ Documentation ### [php.ini settings](https://github.com/JosephLenton/PHP-Error/wiki/php.ini) -Do not use on a live site! --------------------------- - -This is intended for __development only__. It shows more about your site, gives you more info, and makes trivial errors fatal. -All of that is awesome if you want to debug quicker, and force high quality standards. - -On a production server, that sucks, and is potentially unsafe. - -In case you forget, you can disable this in production using the 'php_error.force_disabled' php.ini option (see below). - Advanced Features ----------------- diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..1e18964 --- /dev/null +++ b/composer.json @@ -0,0 +1,31 @@ +{ + "name": "joseph-lenton/php-error", + "description": "Better error reporting for PHP, and prettier too!", + "version": "1.0.0", + "homepage": "https://github.com/JosephLenton/PHP-Error", + "license": "BSD-3-Clause", + "require": { + "php": ">=5.3.0" + }, + "repositories": [ + { + "type": "git", + "url": "https://github.com/JosephLenton/PHP-Error" + } + ], + "keywords": [ + "php", + "error", + "reporting" + ], + "authors": [ + { + "name": "Joseph Lenton", + "homepage": "http://www.playmycode.com/", + "role": "Developer" + } + ], + "autoload": { + "files": ["src/php_error.php"] + } +} \ No newline at end of file diff --git a/src/php_error.php b/src/php_error.php index fa55cd5..1796913 100644 --- a/src/php_error.php +++ b/src/php_error.php @@ -1,12 +1,12 @@ nor the * names of its 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 @@ -28,7 +28,7 @@ * 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. - * + * * Uses: * JSMin-php https://github.com/rgrove/jsmin-php/ * jQuery http://jquery.com/ @@ -36,62 +36,61 @@ /** * PHP Error - * + * * --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - * + * * WARNING! It is downright _DANGEROUS_ to use this in production, on * a live website. It should *ONLY* be used for development. - * + * * PHP Error will kill your environment at will, clear the output * buffers, and allows HTML injection from exceptions. - * + * * In future versions it plans to do far more then that. - * + * * If you use it in development, awesome! If you use it in production, * you're an idiot. - * + * * --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - * + * * = Info - * + * * A small API for replacing the standard PHP errors, with prettier * error reporting. This will change the error reporting level, and this * is deliberate, as I believe in strict development errors. - * + * * simple usage: - * + * * \php_error\reportErrors(); - * + * * Advanced example: - * + * * There is more too it if you want more customized error handling. You * can pass in options, to customize the setup, and you get back a * handler you can alter at runtime. - * + * * $handler = new \php_error\ErrorHandler( $myOptions ); * $handler->turnOn(); - * + * * There should only ever be one handler! This is an (underdstandable) * limitation in PHP. It's because if an exception or error is raised, * then there is a single point of handling it. - * + * * = INI Options - * + * * - php_error.force_disabled When set to a true value (such as on), * this forces this to be off. * This is so you can disable this script * in your production servers ini file, * incase you accidentally upload this there. - * + * * --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- - * + * * @author Joseph Lenton | https://github.com/josephlenton */ namespace php_error; - use \php_error\ErrorException, - \php_error\FileLinesSet, + use \php_error\FileLinesSet, \php_error\ErrorHandler, \php_error\JSMin, @@ -99,6 +98,7 @@ use \Closure, \Exception, + \ErrorException, \InvalidArgumentException; use \ReflectionMethod, @@ -117,35 +117,32 @@ /* * These are used as token identifiers by PHP. - * + * * If they are missing, then they should never pop out of PHP, * so we just give them their future value. - * + * * They are primarily here so I don't have to alter the 5.3 * compliant code. Instead I can delete pre-5.3 code (this * code), in the future. - * + * * As long as the value is unique, and does not clash with PHP, * then any number could be used. That is why they start counting * at 100,000. */ - if ( ! defined('T_DIR') ) { - define( 'T_DIR', 100001 ); - } - if ( ! defined('T_GOTO') ) { - define( 'T_GOTO', 100002 ); - } - if ( ! defined('T_NAMESPACE') ) { - define( 'T_NAMESPACE', 100003 ); - } - if ( ! defined('T_NS_C') ) { - define( 'T_NS_C', 100004 ); - } - if ( ! defined('T_NS_SEPARATOR') ) { - define( 'T_NS_SEPARATOR', 100005 ); - } - if ( ! defined('T_USE') ) { - define( 'T_USE', 100006 ); + + $missingIdentifier = array( + 'T_INSTEADOF', + 'T_TRAIT', + 'T_TRAIT_C', + 'T_YIELD', + 'T_FINALLY' + ); + + $counter = 100001; + foreach ( $missingIdentifier as $id ) { + if ( ! defined($id) ) { + define( $id, $counter++ ); + } } /* @@ -161,7 +158,7 @@ * check that display errors is on * and ensure we are *not* a command line script. */ - $_php_error_is_ini_enabled = + $_php_error_is_ini_enabled = ! @get_cfg_var( 'php_error.force_disabled' ) && ! @get_cfg_var( 'php_error.force_disable' ) && @ini_get('display_errors') === '1' && @@ -172,10 +169,10 @@ /** * This is shorthand for turning off error handling, * calling a block of code, and then turning it on. - * + * * However if 'reportErrors' has not been called, * then this will silently do nothing. - * + * * @param callback A PHP function to call. * @return The result of calling the callback. */ @@ -191,19 +188,19 @@ function withoutErrors( $callback ) { /** * Turns on error reporting, and returns the handler. - * + * * If you just want error reporting on, then don't bother * catching the handler. If you're building something * clever, like a framework, then you might want to grab * and use it. - * + * * Note that calling this a second time will replace the * global error handling with a new error handler. * The existing one will be turned off, and the new one * turned on. - * + * * You can't use two at once! - * + * * @param options Optional, options declaring how PHP Error should be setup and used. * @return The ErrorHandler used for reporting errors. */ @@ -227,18 +224,18 @@ class ErrorHandler * {closure}() * blah::foo() * foo() - * + * * It is: * a closure * or a method or function * followed by parenthesis '()' - * + * * a function is 'namespace function' * a method is 'namespace class::function', or 'namespace class->function' * the whole namespace is optional * namespace is made up of an '\' and then repeating 'namespace\' * both the first slash, and the repeating 'namespace\', are optional - * + * * 'END' matches it at the end of a string, the other one does not. */ const REGEX_METHOD_OR_FUNCTION_END = '/(\\{closure\\})|(((\\\\)?(\b[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\\\\)*)?\b[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(::[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)?)\\(\\)$/'; @@ -253,7 +250,7 @@ class ErrorHandler * where the error is reported. This is the number * of lines around the line in question, * including that line. - * + * * So '9' will be the error line + 4 lines above + 4 lines below. */ const NUM_FILE_LINES = 13; @@ -262,16 +259,28 @@ class ErrorHandler const FILE_TYPE_IGNORE = 2; const FILE_TYPE_ROOT = 3; + /* + * These are the various magic identifiers, + * used for headers, post requests, and so on. + * + * Their main purpose is to be long and more or less unique, + * enough that a collision with user code is rare. + */ + const PHP_ERROR_MAGIC_HEADER_KEY = 'PHP_ERROR_MAGIC_HEADER'; const PHP_ERROR_MAGIC_HEADER_VALUE = 'php_stack_error'; const MAGIC_IS_PRETTY_ERRORS_MARKER = ''; + const HEADER_SAVE_FILE = 'PHP_ERROR_SAVE_FILES'; + + const POST_FILE_LOCATION = 'php_error_upload_file'; + const PHP_ERROR_INI_PREFIX = 'php_error'; /** * At the time of writing, scalar type hints are unsupported. * By scalar, I mean 'string' and 'integer'. - * + * * If they do get added, this is here as a trap to turn scalar * type hint warnings on and off. */ @@ -335,6 +344,7 @@ class ErrorHandler 'T_EXTENDS' => 'extends', 'T_FILE' => '__FILE__', 'T_FINAL' => 'final', + 'T_FINALLY' => 'finally', 'T_FOR' => 'for', 'T_FOREACH' => 'foreach', 'T_FUNCTION' => 'function', @@ -348,6 +358,7 @@ class ErrorHandler 'T_INCLUDE' => 'include', 'T_INCLUDE_ONCE' => 'include_once', 'T_INSTANCEOF' => 'instanceof', + 'T_INSTEADOF' => 'insteadof', 'T_INT_CAST' => 'int cast', 'T_INTERFACE' => 'interface', 'T_ISSET' => 'isset', @@ -398,6 +409,8 @@ class ErrorHandler 'T_SWITCH' => 'switch', 'T_THROW' => 'throw', 'T_TRY' => 'try', + 'T_TRAIT' => 'trait', + 'T_TRAIT_C' => '__trait__', 'T_UNSET' => 'unset', 'T_UNSET_CAST' => 'unset cast', 'T_USE' => 'use', @@ -405,7 +418,8 @@ class ErrorHandler 'T_VARIABLE' => 'variable', 'T_WHILE' => 'while', 'T_WHITESPACE' => 'whitespace', - 'T_XOR_EQUAL' => "'^='" + 'T_XOR_EQUAL' => "'^='", + 'T_YIELD' => 'yield' ); private static $syntaxMap = array( @@ -428,7 +442,7 @@ class ErrorHandler T_DECLARE => 'syntax-keyword', T_DEFAULT => 'syntax-keyword', T_DO => 'syntax-keyword', - + T_ELSE => 'syntax-keyword', T_ELSEIF => 'syntax-keyword', T_ENDDECLARE => 'syntax-keyword', @@ -440,15 +454,17 @@ class ErrorHandler T_EXTENDS => 'syntax-keyword', T_FINAL => 'syntax-keyword', + T_FINALLY => 'syntax-keyword', T_FOR => 'syntax-keyword', T_FOREACH => 'syntax-keyword', T_FUNCTION => 'syntax-keyword', T_GLOBAL => 'syntax-keyword', T_GOTO => 'syntax-keyword', - + T_IF => 'syntax-keyword', T_IMPLEMENTS => 'syntax-keyword', T_INSTANCEOF => 'syntax-keyword', + T_INSTEADOF => 'syntax-keyword', T_INTERFACE => 'syntax-keyword', T_LOGICAL_AND => 'syntax-keyword', @@ -463,10 +479,12 @@ class ErrorHandler T_STATIC => 'syntax-keyword', T_SWITCH => 'syntax-keyword', T_THROW => 'syntax-keyword', + T_TRAIT => 'syntax-keyword', T_TRY => 'syntax-keyword', T_USE => 'syntax-keyword', T_VAR => 'syntax-keyword', T_WHILE => 'syntax-keyword', + T_YIELD => 'syntax-keyword', // __VAR__ type magic constants T_CLASS_C => 'syntax-literal', @@ -476,6 +494,7 @@ class ErrorHandler T_LINE => 'syntax-literal', T_METHOD_C => 'syntax-literal', T_NS_C => 'syntax-literal', + T_TRAIT_C => 'syntax-literal', T_DNUMBER => 'syntax-literal', T_LNUMBER => 'syntax-literal', @@ -508,7 +527,7 @@ class ErrorHandler /** * A list of methods which are known to call the autoloader, * but should not error, if the class is not found. - * + * * They are allowed to fail, so we don't store a class not * found exception if they do. */ @@ -539,7 +558,17 @@ private static function isIIS() { strpos($_SERVER['_FCGI_X_PIPE_'], 'IISFCGI') !== false ); } - + + private static function isBinaryRequest() { + $response = ErrorHandler::getResponseHeaders(); + + foreach ( $response as $key => $value ) { + if ( strtolower($key) === 'content-transfer-encoding' ) { + return strtolower($value) === 'binary'; + } + } + } + /** * This attempts to state if this is *not* a PHP request, * but it cannot say if it *is* a PHP request. It achieves @@ -552,28 +581,24 @@ private static function isIIS() { private static function isNonPHPRequest() { /* * Check if we are a mime type that isn't allowed. + * + * If an allowed type is found, then we return false, + * as were are a PHP Request. * - * Anything other than 'text/html' or similar will cause - * this to turn off. + * Anything else found, returns true, as that means + * we are dealing with something unknown. */ $response = ErrorHandler::getResponseHeaders(); foreach ( $response as $key => $value ) { if ( strtolower($key) === 'content-type' ) { - $found = true; - foreach ( ErrorHandler::$ALLOWED_RETURN_MIME_TYPES as $type ) { if ( stripos($value, $type) !== false ) { - $found = true; - break; + return false; } } - if ( ! $found ) { - return true; - } - - break; + return true; } } @@ -583,7 +608,7 @@ private static function isNonPHPRequest() { /** * Looks up a description for the symbol given, * and if found, it is returned. - * + * * If it's not found, then the symbol given is returned. */ private static function phpSymbolToDescription( $symbol ) { @@ -596,9 +621,9 @@ private static function phpSymbolToDescription( $symbol ) { /** * Attempts to syntax highlight the code snippet done. - * + * * This is then returned as HTML, ready to be dumped to the screen. - * + * * @param code An array of code lines to syntax highlight. * @return HTML version of the code given, syntax highlighted. */ @@ -702,13 +727,13 @@ private static function syntaxHighlight( $code ) { /** * Splits a given function name into it's 'class, function' parts. * If there is no class, then null is returned. - * + * * It also returns these parts in an array of: array( $className, $functionName ); - * + * * Usage: - * + * * list( $class, $function ) = ErrorHandler::splitFunction( $name ); - * + * * @param name The function name to split. * @return An array containing class and function name. */ @@ -750,7 +775,7 @@ private static function newArgument( $name, $type=false, $isPassedByReference=fa ( $klass ? array( "\\$klass", $functionName ) : $functionName ), - $name->name, + $name->name, true ); @@ -840,12 +865,12 @@ private static function syntaxHighlightFunctionMatch( $match, &$stackTrace, $hig * Returns the values given, as HTML, syntax highlighted. * It's a shorter, slightly faster, more no-nonsense approach * then 'syntaxHighlight'. - * + * * This is for syntax highlighting: * - fun( [args] ) * - class->fun( [args] ) * - class::fun( [args] ) - * + * * Class and type can be null, to denote no class, but are not optional. */ private static function syntaxHighlightFunction( $class, $type, $fun, &$args=null ) { @@ -921,7 +946,7 @@ private static function syntaxHighlightFunction( $class, $type, $fun, &$args=nul /** * Checks if the item is in options, and if it is, then it is removed and returned. - * + * * If it is not found, or if options is not an array, then the alt is returned. */ private static function optionsPop( &$options, $key, $alt=null ) { @@ -1012,7 +1037,7 @@ private static function setFoldersInner( &$newFolders, &$newLongest, $folder ) { $count = count( $parts ); $newLongest = max( $newLongest, $count ); - + if ( isset($newFolders[$count]) ) { $folds = &$newFolders[$count]; $folds[]= $parts; @@ -1026,10 +1051,10 @@ private static function getRequestHeaders() { return getallheaders(); } else { $headers = array(); - + foreach ( $_SERVER as $key => $value ) { if ( strpos($key, 'HTTP_') === 0 ) { - $key = str_replace( " ", "-", ucwords(strtolower( str_replace("_", " ", substr($key, 5)) )) ); + $key = str_replace( " ", "-", ucwords(strtolower( str_replace("_", " ", substr($key, 5)) )) ); $headers[ $key ] = $value; } } @@ -1045,7 +1070,7 @@ private static function getResponseHeaders() { /* * Merge the headers_list into apache_response_headers. - * + * * This is because sometimes things are in one, which are * not present in the other. */ @@ -1099,6 +1124,9 @@ public static function identifyTypeHTML( $arg, $recurseLevels=1 ) { } } + private $saveUrl; + private $isSavingEnabled; + private $cachedFiles; private $isShutdownRegistered; @@ -1137,58 +1165,67 @@ public static function identifyTypeHTML( $arg, $recurseLevels=1 ) { /** * = Options = - * + * * All options are optional, and so is passing in an options item. * You don't have to supply any, it's up to you. - * + * * Note that if 'php_error.force_disable' is true, then this object * will try to look like it works, but won't actually do anything. - * + * * All options can also be passed in from 'php.ini'. You do this * by setting it with 'php_error.' prefix. For example: - * + * * php_error.catch_ajax_errors = On * php_error.error_reporting_on = E_ALL | E_STRICT - * + * * Includes: * = Types of errors this will catch = * - catch_ajax_errors When on, this will inject JS Ajax wrapping code, to allow this to catch any future JSON errors. Defaults to true. * - catch_supressed_errors The @ supresses errors. If set to true, then they are still reported anyway, but respected when false. Defaults to false. * - catch_class_not_found When true, loading a class that does not exist will be caught. This defaults to true. - * + * * = Error reporting level = * - error_reporting_on value for when errors are on, defaults to all errors * - error_reporting_off value for when errors are off, defaults to php.ini's error_reporting. - * + * * = Setup Details = * - application_root When it's working out hte stack trace, this is the root folder of the application, to use as it's base. * Defaults to the servers root directory. - * + * * A relative path can be given, but lets be honest, an explicit path is the way to guarantee that you * will get the path you want. My relative might not be the same as your relative. - * - * - snippet_num_lines The number of lines to display in the code snippet. + * + * - snippet_num_lines The number of lines to display in the code snippet. * That includes the line being reported. - * + * * - server_name The name for this server, defaults to "$_SERVER['SERVER_NAME']" - * + * * - ignore_folders This is allows you to highlight non-framework code in a stack trace. * An array of folders to ignore, when working out the stack trace. * This is folder prefixes in relation to the application_root, whatever that might be. * They are only ignored if there is a file found outside of them. * If you still don't get what this does, don't worry, it's here cos I use it. - * + * * - application_folders Just like ignore, but anything found in these folders takes precedence * over anything else. - * + * * - background_text The text that appeares in the background. By default this is blank. * Why? You can replace this with the name of your framework, for extra customization spice. - * + * * - html_only By default, PHP Error only runs on ajax and HTML pages. * If this is false, then it will also run when on non-HTML * pages too, such as replying with images of JavaScript * from your PHP. Defaults to true. * + * - file_link When true, files are linked to from the CSS Stack trace, allowing you to open them. + * Defaults to true. + * + * - save_url The url of where to send files, to be saved. + * Note that 'enable_saving' must be on for this to be used (which it is by default). + * + * - enable_saving Can be true or false. When true, saving files is enabled, and when false, it is disabled. + * Defaults to true! + * * @param options Optional, an array of values to customize this handler. * @throws Exception This is raised if given an options that does *not* exist (so you know that option is meaningless). */ @@ -1209,7 +1246,7 @@ public function __construct( $options=null ) { /* * Deal with the options. - * + * * They are removed one by one, and any left, will raise an error. */ @@ -1223,11 +1260,14 @@ public function __construct( $options=null ) { ErrorHandler::setFolders( $this->applicationFolders, $this->applicationFoldersLongest, $appFolders ); } - $this->defaultErrorReportingOn = ErrorHandler::optionsPop( $options, 'error_reporting_on' , -1 ); - $this->defaultErrorReportingOff = ErrorHandler::optionsPop( $options, 'error_reporting_off', error_reporting() ); + $this->saveUrl = ErrorHandler::optionsPop( $options, 'save_url', $_SERVER['REQUEST_URI'] ); + $this->isSavingEnabled = ErrorHandler::optionsPop( $options, 'enable_saving', true ); + + $this->defaultErrorReportingOn = ErrorHandler::optionsPop( $options, 'error_reporting_on' , -1 ); + $this->defaultErrorReportingOff = ErrorHandler::optionsPop( $options, 'error_reporting_off' , error_reporting() ); - $this->applicationRoot = ErrorHandler::optionsPop( $options, 'application_root' , $_SERVER['DOCUMENT_ROOT'] ); - $this->serverName = ErrorHandler::optionsPop( $options, 'error_reporting_off', $_SERVER['SERVER_NAME'] ); + $this->applicationRoot = ErrorHandler::optionsPop( $options, 'application_root' , $_SERVER['DOCUMENT_ROOT'] ); + $this->serverName = ErrorHandler::optionsPop( $options, 'server_name' , $_SERVER['SERVER_NAME'] ); /* * Relative paths might be given for document root, @@ -1246,7 +1286,7 @@ public function __construct( $options=null ) { $this->backgroundText = ErrorHandler::optionsPop( $options, 'background_text' , '' ); $this->numLines = ErrorHandler::optionsPop( $options, 'snippet_num_lines' , ErrorHandler::NUM_FILE_LINES ); - $this->displayLineNumber = ErrorHandler::optionsPop( $options, 'display_line_numbers' , false ); + $this->displayLineNumber = ErrorHandler::optionsPop( $options, 'display_line_numbers' , true ); $this->htmlOnly = !! ErrorHandler::optionsPop( $options, 'html_only', true ); @@ -1258,6 +1298,11 @@ public function __construct( $options=null ) { $this->defaultErrorReportingOn = E_ERROR | E_WARNING | E_PARSE | E_USER_DEPRECATED & ~E_DEPRECATED & ~E_STRICT; } + $concrete5 = ErrorHandler::optionsPop( $options, 'concrete5', false ); + if ( $concrete5 ) { + $this->defaultErrorReportingOn = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED; + } + if ( $options ) { foreach ( $options as $key => $val ) { throw new InvalidArgumentException( "Unknown option given $key" ); @@ -1300,27 +1345,50 @@ public function isOff() { /** * Turns error reporting on. - * + * * This will use the strictest error reporting available, or the * level you pass in when creating this using the 'error_reporting_on' * option. - * + * * @return This error reporting handler, for method chaining. */ public function turnOn() { $this->propagateTurnOff(); $this->setEnabled( true ); + /* + * Check if file changes have been uploaded, + * and if so, save them. + */ + global $_php_error_is_ini_enabled; + if ( $_php_error_is_ini_enabled ) { + if ( $this->isSavingEnabled ) { + $headers = ErrorHandler::getRequestHeaders(); + + if ( isset($headers[ErrorHandler::HEADER_SAVE_FILE]) ) { + if ( isset($_POST) && isset($_POST[ErrorHandler::POST_FILE_LOCATION]) ) { + $files = $_POST[ErrorHandler::POST_FILE_LOCATION]; + + foreach ( $files as $file => $content ) { + @file_put_contents( $file, stripcslashes($content) ); + } + + exit(0); + } + } + } + } + return $this; } /** * Turns error reporting off. - * + * * This will use the 'php.ini' setting for the error_reporting level, * or one you have passed in if you used the 'error_reporting_off' * option when creating this. - * + * * @return This error reporting handler, for method chaining. */ public function turnOff() { @@ -1333,19 +1401,19 @@ public function turnOff() { * Allows you to run a callback with strict errors turned off. * Standard errors still apply, but this will use the default * error and exception handlers. - * + * * This is useful for when loading libraries which do not * adhere to strict errors, such as Wordpress. - * + * * To use: - * + * * withoutErrors( function() { * // unsafe code here * }); - * + * * This will use the error_reporting value for when this is * turned off. - * + * * @param callback A PHP function to call. * @return The result of calling the callback. */ @@ -1364,16 +1432,16 @@ public function withoutErrors( $callback ) { return $callback(); } } - + /** - * This is the shutdown function, which should *only* be called + * This is the shutdown function, which should *only* be called * via 'register_shutdown_function'. - * + * * It's exposed because it has to be exposed. */ public function __onShutdown() { global $_php_error_is_ini_enabled; - + if ( $_php_error_is_ini_enabled ) { if ( $this->isOn() ) { $error = error_get_last(); @@ -1409,21 +1477,21 @@ private function propagateTurnOff() { $this->lastGlobalErrorHandler = null; } } - + /** * This is intended to be used closely with 'onShutdown'. * It ensures that output buffering is turned on. - * + * * Why? The user may output content, and *then* hit an error. * We cannot replace the page if this happens, * because they have already outputted information. - * + * * So we buffer the page, and then output at the end of the page, * or when an error strikes. */ private function startBuffer() { global $_php_error_is_ini_enabled; - + if ( $_php_error_is_ini_enabled && !$this->isBufferSetup ) { $this->isBufferSetup = true; @@ -1461,7 +1529,7 @@ private function startBuffer() { /** * Turns off buffering, and discards anything buffered * so far. - * + * * This will return what has been buffered incase you * do want it. However otherwise, it will be lost. */ @@ -1477,7 +1545,7 @@ private function discardBuffer() { /** * Flushes the internal buffer, * outputting what is left. - * + * * @param append Optional, extra content to append onto the output buffer. */ private function flushBuffer() { @@ -1491,7 +1559,7 @@ private function flushBuffer() { * This will finish buffering, and output the page. * It also appends the magic JS onto the beginning of the page, * if enabled, to allow working with Ajax. - * + * * Note that if PHP Error has been disabled in the php.ini file, * or through some other option, such as running from the command line, * then this will do nothing (as no buffering will take place). @@ -1525,10 +1593,11 @@ public function endBuffer() { ob_start(); } - if ( - !$this->isAjax && - $this->catchAjaxErrors && - (!$this->htmlOnly || !ErrorHandler::isNonPHPRequest()) + if ( + !$this->isAjax && + $this->catchAjaxErrors && + (!$this->htmlOnly || !ErrorHandler::isNonPHPRequest()) && + !ErrorHandler::isBinaryRequest() ) { $js = $this->getContent( 'displayJSInjection' ); $js = JSMin::minify( $js ); @@ -1552,7 +1621,7 @@ public function endBuffer() { /** * Calls the given method on this object, * captures it's output, and then returns it. - * + * * @param method The name of the method to call. * @return All of the text outputted during the method call. */ @@ -1602,14 +1671,14 @@ private function getFolderType( $root, $file ) { /** * Finds the file named, and returns it's contents in an array. - * + * * It's essentially the same as 'file_get_contents'. However * this will add caching at this PHP layer, avoiding lots of * duplicate calls. - * + * * It also splits the file into an array of lines, and makes * it html safe. - * + * * @param path The file to get the contents of. * @return The file we are after, as an array of lines. */ @@ -1641,9 +1710,9 @@ private function getFileContents( $path ) { /** * Reads out the code from the section of the line, * which is at fault. - * + * * The array is in a mapping of: array( line-number => line ) - * + * * If something goes wrong, then null is returned. */ private function readCodeFile( $errFile, $errLine ) { @@ -1655,7 +1724,7 @@ private function readCodeFile( $errFile, $errLine ) { $searchUp = ceil( $numLines*0.75 ); $searchDown = $numLines - $searchUp; - + $countLines = count( $lines ); /* @@ -1738,14 +1807,14 @@ private function readCodeFile( $errFile, $errLine ) { /** * Attempts to remove the root path from the path given. * If the path can't be removed, then the original path is returned. - * + * * For example if root is 'C:/users/projects/my_site', * and the file is 'C:/users/projects/my_site/index.php', * then the root is removed, and we are left with just 'index.php'. - * + * * This is to remove line noise; you don't need to be told the * 'C:/whatever' bit 20 times. - * + * * @param root The root path to remove. * @param path The file we are removing the root section from. */ @@ -1764,7 +1833,7 @@ private function removeRootPath( $root, $path ) { /** * Parses, and alters, the errLine, errFile and message given. - * + * * This includes adding syntax highlighting, removing duplicate * information we already have, and making the error easier to * read. @@ -1794,7 +1863,7 @@ private function improveErrorMessage( $ex, $code, $message, $errLine, $errFile, /* * This is for calling a function that doesn't exist. - * + * * The message contains a long description of where this takes * place, even though we are already told this through line and * file info. So we cut it out. @@ -1915,7 +1984,7 @@ private function improveErrorMessage( $ex, $code, $message, $errLine, $errFile, /* * Unexpected symbol errors. * For example 'unexpected T_OBJECT_OPERATOR'. - * + * * This swaps the 'T_WHATEVER' for the symbolic representation. */ } else if ( $code === 4 ) { @@ -1925,7 +1994,7 @@ private function improveErrorMessage( $ex, $code, $message, $errLine, $errFile, $semiColonError = false; if ( strpos($message, 'syntax error,') === 0 && $errLine > 2 ) { $lines = ErrorHandler::getFileContents( $errFile ); - + $line = $lines[$errLine-1]; if ( preg_match( ErrorHandler::REGEX_MISSING_SEMI_COLON_FOLLOWING_LINE, $line ) !== 0 ) { $content = rtrim( join( "\n", array_slice($lines, 0, $errLine-1) ) ); @@ -2073,11 +2142,11 @@ private function improveErrorMessage( $ex, $code, $message, $errLine, $errFile, * The code above can prioritize a location in the stack trace, * this is 'stackSearchI'. So we should start our search from there, * and work down the stack. - * + * * This is built in a way so that when it reaches the end, it'll loop * back round to the beginning, and check the traces we didn't check * last time. - * + * * If stackSearchI was not altered, then it just searches from top * through to the bottom. */ @@ -2110,7 +2179,7 @@ private function improveErrorMessage( $ex, $code, $message, $errLine, $errFile, /** * Parses the stack trace, and makes it look pretty. - * + * * This includes adding in the syntax highlighting, * highlighting the colours for the files, * and padding with whitespace. @@ -2172,7 +2241,7 @@ private function parseStackTrace( $code, $message, $errLine, $errFile, &$stackTr trim( $contents[$trace['line']-1] ) ); } - } + } $trace['info'] = $info; @@ -2235,6 +2304,8 @@ private function parseStackTrace( $code, $message, $errLine, $errFile, &$stackTr } // line + file + info + $file = trim( $file ); + $stackStr = "