@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Pass all Throwables to Exception Handlers, not just Exceptions

Summary:
Ref T12855. PHP7 introduced "Throwables", which are sort of like super exceptions. Some errors that PHP raises at runtime have become Throwables instead of old-school errors now.

The major effect this has is blank pages during development under PHP7 for certain classes of errors: they skip all the nice "show a pretty error" handlers and

This isn't a compelete fix, but catches the most common classes of unexpected Throwable and sends them through the normal machinery. Principally, it shows a nice stack trace again instead of a blank page for a larger class of typos and minor mistakes.

Test Plan:
Before: blank page. After:

{F5007979}

Reviewers: chad, amckinley

Reviewed By: chad

Maniphest Tasks: T12855

Differential Revision: https://secure.phabricator.com/D18136

+72 -72
+9 -6
src/aphront/configuration/AphrontApplicationConfiguration.php
··· 270 270 } 271 271 } catch (Exception $ex) { 272 272 $original_exception = $ex; 273 - $response = $this->handleException($ex); 273 + $response = $this->handleThrowable($ex); 274 + } catch (Throwable $ex) { 275 + $original_exception = $ex; 276 + $response = $this->handleThrowable($ex); 274 277 } 275 278 276 279 try { ··· 663 666 * This method delegates exception handling to available subclasses of 664 667 * @{class:AphrontRequestExceptionHandler}. 665 668 * 666 - * @param Exception Exception which needs to be handled. 669 + * @param Throwable Exception which needs to be handled. 667 670 * @return wild Response or response producer, or null if no available 668 671 * handler can produce a response. 669 672 * @task exception 670 673 */ 671 - private function handleException(Exception $ex) { 674 + private function handleThrowable($throwable) { 672 675 $handlers = AphrontRequestExceptionHandler::getAllHandlers(); 673 676 674 677 $request = $this->getRequest(); 675 678 foreach ($handlers as $handler) { 676 - if ($handler->canHandleRequestException($request, $ex)) { 677 - $response = $handler->handleRequestException($request, $ex); 679 + if ($handler->canHandleRequestThrowable($request, $throwable)) { 680 + $response = $handler->handleRequestThrowable($request, $throwable); 678 681 $this->validateErrorHandlerResponse($handler, $response); 679 682 return $response; 680 683 } 681 684 } 682 685 683 - throw $ex; 686 + throw $throwable; 684 687 } 685 688 686 689 private static function newSelfCheckResponse() {
+4 -10
src/aphront/handler/AphrontRequestExceptionHandler.php
··· 12 12 13 13 abstract public function getRequestExceptionHandlerPriority(); 14 14 15 - public function shouldLogException( 16 - AphrontRequest $request, 17 - Exception $ex) { 18 - return null; 19 - } 20 - 21 - abstract public function canHandleRequestException( 15 + abstract public function canHandleRequestThrowable( 22 16 AphrontRequest $request, 23 - Exception $ex); 17 + $throwable); 24 18 25 - abstract public function handleRequestException( 19 + abstract public function handleRequestThrowable( 26 20 AphrontRequest $request, 27 - Exception $ex); 21 + $throwable); 28 22 29 23 final public static function getAllHandlers() { 30 24 return id(new PhutilClassMapQuery())
+8 -7
src/aphront/handler/PhabricatorAjaxRequestExceptionHandler.php
··· 11 11 return pht('Responds to requests made by AJAX clients.'); 12 12 } 13 13 14 - public function canHandleRequestException( 14 + public function canHandleRequestThrowable( 15 15 AphrontRequest $request, 16 - Exception $ex) { 16 + $throwable) { 17 17 // For non-workflow requests, return a Ajax response. 18 18 return ($request->isAjax() && !$request->isWorkflow()); 19 19 } 20 20 21 - public function handleRequestException( 21 + public function handleRequestThrowable( 22 22 AphrontRequest $request, 23 - Exception $ex) { 23 + $throwable) { 24 24 25 25 // Log these; they don't get shown on the client and can be difficult 26 26 // to debug. 27 - phlog($ex); 27 + phlog($throwable); 28 28 29 29 $response = new AphrontAjaxResponse(); 30 30 $response->setError( 31 31 array( 32 - 'code' => get_class($ex), 33 - 'info' => $ex->getMessage(), 32 + 'code' => get_class($throwable), 33 + 'info' => $throwable->getMessage(), 34 34 )); 35 + 35 36 return $response; 36 37 } 37 38
+6 -6
src/aphront/handler/PhabricatorConduitRequestExceptionHandler.php
··· 11 11 return pht('Responds to requests made by Conduit clients.'); 12 12 } 13 13 14 - public function canHandleRequestException( 14 + public function canHandleRequestThrowable( 15 15 AphrontRequest $request, 16 - Exception $ex) { 16 + $throwable) { 17 17 return $request->isConduit(); 18 18 } 19 19 20 - public function handleRequestException( 20 + public function handleRequestThrowable( 21 21 AphrontRequest $request, 22 - Exception $ex) { 22 + $throwable) { 23 23 24 24 $response = id(new ConduitAPIResponse()) 25 - ->setErrorCode(get_class($ex)) 26 - ->setErrorInfo($ex->getMessage()); 25 + ->setErrorCode(get_class($throwable)) 26 + ->setErrorInfo($throwable->getMessage()); 27 27 28 28 return id(new AphrontJSONResponse()) 29 29 ->setAddJSONShield(false)
+11 -11
src/aphront/handler/PhabricatorDefaultRequestExceptionHandler.php
··· 11 11 return pht('Handles all other exceptions.'); 12 12 } 13 13 14 - public function canHandleRequestException( 14 + public function canHandleRequestThrowable( 15 15 AphrontRequest $request, 16 - Exception $ex) { 16 + $throwable) { 17 17 18 18 if (!$this->isPhabricatorSite($request)) { 19 19 return false; ··· 22 22 return true; 23 23 } 24 24 25 - public function handleRequestException( 25 + public function handleRequestThrowable( 26 26 AphrontRequest $request, 27 - Exception $ex) { 27 + $throwable) { 28 28 29 29 $viewer = $this->getViewer($request); 30 30 ··· 33 33 // the internet. These include requests with bad CSRF tokens and 34 34 // questionable "Host" headers. 35 35 $should_log = true; 36 - if ($ex instanceof AphrontMalformedRequestException) { 37 - $should_log = !$ex->getIsUnlogged(); 36 + if ($throwable instanceof AphrontMalformedRequestException) { 37 + $should_log = !$throwable->getIsUnlogged(); 38 38 } 39 39 40 40 if ($should_log) { 41 - phlog($ex); 41 + phlog($throwable); 42 42 } 43 43 44 - $class = get_class($ex); 45 - $message = $ex->getMessage(); 44 + $class = get_class($throwable); 45 + $message = $throwable->getMessage(); 46 46 47 - if ($ex instanceof AphrontSchemaQueryException) { 47 + if ($throwable instanceof AphrontSchemaQueryException) { 48 48 $message .= "\n\n".pht( 49 49 "NOTE: This usually indicates that the MySQL schema has not been ". 50 50 "properly upgraded. Run '%s' to ensure your schema is up to date.", ··· 54 54 if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) { 55 55 $trace = id(new AphrontStackTraceView()) 56 56 ->setUser($viewer) 57 - ->setTrace($ex->getTrace()); 57 + ->setTrace($throwable->getTrace()); 58 58 } else { 59 59 $trace = null; 60 60 }
+8 -8
src/aphront/handler/PhabricatorHighSecurityRequestExceptionHandler.php
··· 13 13 'to present MFA credentials to take an action.'); 14 14 } 15 15 16 - public function canHandleRequestException( 16 + public function canHandleRequestThrowable( 17 17 AphrontRequest $request, 18 - Exception $ex) { 18 + $throwable) { 19 19 20 20 if (!$this->isPhabricatorSite($request)) { 21 21 return false; 22 22 } 23 23 24 - return ($ex instanceof PhabricatorAuthHighSecurityRequiredException); 24 + return ($throwable instanceof PhabricatorAuthHighSecurityRequiredException); 25 25 } 26 26 27 - public function handleRequestException( 27 + public function handleRequestThrowable( 28 28 AphrontRequest $request, 29 - Exception $ex) { 29 + $throwable) { 30 30 31 31 $viewer = $this->getViewer($request); 32 32 33 33 $form = id(new PhabricatorAuthSessionEngine())->renderHighSecurityForm( 34 - $ex->getFactors(), 35 - $ex->getFactorValidationResults(), 34 + $throwable->getFactors(), 35 + $throwable->getFactorValidationResults(), 36 36 $viewer, 37 37 $request); 38 38 ··· 61 61 'period of time. When you are finished taking sensitive '. 62 62 'actions, you should leave high security.')) 63 63 ->setSubmitURI($request->getPath()) 64 - ->addCancelButton($ex->getCancelURI()) 64 + ->addCancelButton($throwable->getCancelURI()) 65 65 ->addSubmitButton(pht('Enter High Security')); 66 66 67 67 $request_parameters = $request->getPassthroughRequestParameters(
+12 -10
src/aphront/handler/PhabricatorPolicyRequestExceptionHandler.php
··· 13 13 'do something they do not have permission to do.'); 14 14 } 15 15 16 - public function canHandleRequestException( 16 + public function canHandleRequestThrowable( 17 17 AphrontRequest $request, 18 - Exception $ex) { 18 + $throwable) { 19 19 20 20 if (!$this->isPhabricatorSite($request)) { 21 21 return false; 22 22 } 23 23 24 - return ($ex instanceof PhabricatorPolicyException); 24 + return ($throwable instanceof PhabricatorPolicyException); 25 25 } 26 26 27 - public function handleRequestException( 27 + public function handleRequestThrowable( 28 28 AphrontRequest $request, 29 - Exception $ex) { 29 + $throwable) { 30 30 31 31 $viewer = $this->getViewer($request); 32 32 ··· 52 52 array( 53 53 'class' => 'aphront-policy-rejection', 54 54 ), 55 - $ex->getRejection()), 55 + $throwable->getRejection()), 56 56 ); 57 57 58 58 $list = null; 59 - if ($ex->getCapabilityName()) { 60 - $list = $ex->getMoreInfo(); 59 + if ($throwable->getCapabilityName()) { 60 + $list = $throwable->getMoreInfo(); 61 61 foreach ($list as $key => $item) { 62 62 $list[$key] = $item; 63 63 } ··· 67 67 array( 68 68 'class' => 'aphront-capability-details', 69 69 ), 70 - pht('Users with the "%s" capability:', $ex->getCapabilityName())); 70 + pht( 71 + 'Users with the "%s" capability:', 72 + $throwable->getCapabilityName())); 71 73 72 74 } 73 75 74 76 $dialog = id(new AphrontDialogView()) 75 - ->setTitle($ex->getTitle()) 77 + ->setTitle($throwable->getTitle()) 76 78 ->setClass('aphront-access-dialog') 77 79 ->setUser($viewer) 78 80 ->appendChild($content);
+7 -7
src/aphront/handler/PhabricatorRateLimitRequestExceptionHandler.php
··· 13 13 'does something too frequently.'); 14 14 } 15 15 16 - public function canHandleRequestException( 16 + public function canHandleRequestThrowable( 17 17 AphrontRequest $request, 18 - Exception $ex) { 18 + $throwable) { 19 19 20 20 if (!$this->isPhabricatorSite($request)) { 21 21 return false; 22 22 } 23 23 24 - return ($ex instanceof PhabricatorSystemActionRateLimitException); 24 + return ($throwable instanceof PhabricatorSystemActionRateLimitException); 25 25 } 26 26 27 - public function handleRequestException( 27 + public function handleRequestThrowable( 28 28 AphrontRequest $request, 29 - Exception $ex) { 29 + $throwable) { 30 30 31 31 $viewer = $this->getViewer($request); 32 32 ··· 34 34 ->setTitle(pht('Slow Down!')) 35 35 ->setUser($viewer) 36 36 ->setErrors(array(pht('You are being rate limited.'))) 37 - ->appendParagraph($ex->getMessage()) 38 - ->appendParagraph($ex->getRateExplanation()) 37 + ->appendParagraph($throwable->getMessage()) 38 + ->appendParagraph($throwable->getRateExplanation()) 39 39 ->addCancelButton('/', pht('Okaaaaaaaaaaaaaay...')); 40 40 } 41 41
+7 -7
src/infrastructure/cluster/exception/PhabricatorClusterExceptionHandler.php
··· 11 11 return pht('Handles runtime problems with cluster configuration.'); 12 12 } 13 13 14 - public function canHandleRequestException( 14 + public function canHandleRequestThrowable( 15 15 AphrontRequest $request, 16 - Exception $ex) { 17 - return ($ex instanceof PhabricatorClusterException); 16 + $throwable) { 17 + return ($throwable instanceof PhabricatorClusterException); 18 18 } 19 19 20 - public function handleRequestException( 20 + public function handleRequestThrowable( 21 21 AphrontRequest $request, 22 - Exception $ex) { 22 + $throwable) { 23 23 24 24 $viewer = $this->getViewer($request); 25 25 26 - $title = $ex->getExceptionTitle(); 26 + $title = $throwable->getExceptionTitle(); 27 27 28 28 $dialog = id(new AphrontDialogView()) 29 29 ->setTitle($title) 30 30 ->setUser($viewer) 31 - ->appendParagraph($ex->getMessage()) 31 + ->appendParagraph($throwable->getMessage()) 32 32 ->addCancelButton('/', pht('Proceed With Caution')); 33 33 34 34 return id(new AphrontDialogResponse())