aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.env.dist9
-rw-r--r--lib/classes/ExceptionDisplay.php82
-rw-r--r--lib/visual.inc.php22
-rw-r--r--templates/unhandled_exception.php5
4 files changed, 98 insertions, 20 deletions
diff --git a/.env.dist b/.env.dist
index e22ca4d..7a425cb 100644
--- a/.env.dist
+++ b/.env.dist
@@ -5,6 +5,15 @@
# DEBUG_BAR="1" // Enable to display the debug bar in development mode
+# Enable the following to allow opening files from exception displays in your
+# editor. Beware: You need to provide a full path for prefix your files since
+# the exception only displays the relative path.
+#
+# Variables being substituted: %{file} and %{line}
+#
+# EDITOR_URL="phpstorm://open?file=<path-to-your-studip>/%{file}&line=%{line}"
+# EDITOR_URL="vscode://file/<path-to-your-studip>/%{file}:%{line}:0
+
# STUDIP_CACHING_ENABLE=""
# STUDIP_CACHE_IS_SESSION_STORAGE=""
# STUDIP_ENV=""
diff --git a/lib/classes/ExceptionDisplay.php b/lib/classes/ExceptionDisplay.php
new file mode 100644
index 0000000..7001a4c
--- /dev/null
+++ b/lib/classes/ExceptionDisplay.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * The ExceptionDisplay class is used to dump an exception as a string for
+ * display.
+ *
+ * By setting the environment variable EDITOR_URL you may activate linking the
+ * file locations to your editor.
+ *
+ * @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
+ * @since Stud.IP 6.1
+ */
+final class ExceptionDisplay implements Stringable
+{
+ private const MARKUP_REGEXP = '~(?<file>(?:\w+/)*\w+\.\w+)(?:\((?<line0>\d+)\)| on line (?<line1>\d+))~m';
+
+ public static function from(Throwable $e): self
+ {
+ return new self($e);
+ }
+
+ private ?string $editor_url;
+
+ private function __construct(
+ private Throwable $exception
+ ) {
+ $this->editor_url = $_ENV['EDITOR_URL'] ?? null;
+ }
+
+ private function reducePathToRelative(string $input): string
+ {
+ return str_replace($GLOBALS['STUDIP_BASE_PATH'] . '/', '', $input);
+ }
+
+ public function display(bool $as_html = false, bool $deep = false): string
+ {
+ $result = '';
+ $result .= sprintf("%s: %s\n", _('Typ'), get_class($this->exception));
+ $result .= sprintf("%s: %s\n", _('Nachricht'), $this->reducePathToRelative($this->exception->getMessage()));
+ $result .= sprintf("%s: %d\n", _('Code'), $this->exception->getCode());
+
+ $trace = sprintf("#$ %s(%u)\n", $this->exception->getFile(), $this->exception->getLine());
+ $trace .= $this->exception->getTraceAsString();
+
+ $result .= sprintf("%s:\n%s\n", _('Stack trace'), $this->reducePathToRelative($trace));
+
+ if ($deep && $this->exception->getPrevious()) {
+ $result .= "\n";
+ $result .= _('Vorherige Exception:') . "\n";
+ $result .= self::from($this->exception->getPrevious())->display(false, $deep);
+ }
+
+ if (!$as_html) {
+ return $result;
+ }
+
+ $result = htmlReady($result, br: true);
+
+ if (Studip\ENV === 'development' && $this->editor_url) {
+ $result = preg_replace_callback(
+ self::MARKUP_REGEXP,
+ function ($matches) {
+ return studip_interpolate('<a href="%{link}">%{label}</a>', [
+ 'label' => $matches['file'] . '(' . ($matches['line0'] ?: $matches['line1']) . ')',
+ 'link' => studip_interpolate($this->editor_url, [
+ 'file' => $matches['file'],
+ 'line' => $matches['line0'] ?: $matches['line1'],
+ ]),
+ ]);
+ },
+ $result
+ );
+ }
+
+ return $result;
+
+ }
+
+ public function __toString(): string
+ {
+ return $this->display();
+ }
+}
diff --git a/lib/visual.inc.php b/lib/visual.inc.php
index 15b1c7d..3f25ace 100644
--- a/lib/visual.inc.php
+++ b/lib/visual.inc.php
@@ -485,31 +485,15 @@ function TransformInternalLinks($str){
/**
* Displays the provided exception in a more readable fashion.
*
- * @param Exception $exception The exception to be displayed
+ * @param Throwable $exception The exception to be displayed
* @param bool $as_html Indicates whether the exception shall be displayed as
* plain text or html (optional, defaults to plain text)
* @param bool $deep Indicates whether any previous exception should be
* included in the output (optional, defaults to false)
* @return String The exception display either as plain text or html
*/
-function display_exception($exception, $as_html = false, $deep = false) {
- $result = '';
- $result .= sprintf("%s: %s\n", _('Typ'), get_class($exception));
- $result .= sprintf("%s: %s\n", _('Nachricht'), $exception->getMessage());
- $result .= sprintf("%s: %d\n", _('Code'), $exception->getCode());
-
- $trace = sprintf(" #$ %s(%u)\n", $exception->getFile(), $exception->getLine())
- . ' ' . str_replace("\n", "\n ", $exception->getTraceAsString());
- $trace = str_replace($GLOBALS['STUDIP_BASE_PATH'] . '/', '', $trace);
- $result .= sprintf("%s:\n%s\n", _('Stack trace'), $trace);
-
- if ($deep && $exception->getPrevious()) {
- $result .= "\n";
- $result .= _('Vorherige Exception:') . "\n";
- $result .= display_exception($exception->getPrevious(), false, $deep);
- }
-
- return $as_html ? nl2br(htmlReady($result)) : $result;
+function display_exception(Throwable $exception, bool $as_html = false, bool $deep = false): string {
+ return ExceptionDisplay::from($exception)->display($as_html, $deep);
}
/**
diff --git a/templates/unhandled_exception.php b/templates/unhandled_exception.php
index b6f16c8..e61438b 100644
--- a/templates/unhandled_exception.php
+++ b/templates/unhandled_exception.php
@@ -1,4 +1,7 @@
<?php
+/**
+ * @var Throwable $exception
+ */
$current_page = _('Fehler');
$title = _('Fehler! Bitte wenden Sie sich an Ihren Systemadministrator.');
@@ -6,7 +9,7 @@ $details = [htmlReady($exception->getMessage())];
if (Studip\ENV == 'development') {
$title = "Houston, we've got a problem.";
- $details = [display_exception($exception, true, true)];
+ $details = [ExceptionDisplay::from($exception)->display(true, true)];
}
?>
<?= MessageBox::exception($title, $details) ?>