From 8b5dd507b3648fc64dd75e23935a14bd16918824 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 17 Sep 2025 13:41:30 +0800 Subject: [PATCH] MDL-86665 core: Fix path stripping in format_backtrace --- public/lib/dml/tests/dml_test.php | 16 ++++++++-------- public/lib/setuplib.php | 18 +++++++++++------- public/lib/tests/setuplib_test.php | 21 ++++++++++++++++++++- 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/public/lib/dml/tests/dml_test.php b/public/lib/dml/tests/dml_test.php index 2a98f0fcd40..8ce80ba08b1 100644 --- a/public/lib/dml/tests/dml_test.php +++ b/public/lib/dml/tests/dml_test.php @@ -501,7 +501,7 @@ final class dml_test extends \database_driver_testcase { $out = $fixture->four($sql); $expected = <<invoke\(\) +-- line \d+ of /public/lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to ReflectionMethod->invoke\(\) EOD; $this->assertMatchesRegularExpression('@' . $this->unix_to_os_dirsep($expected) . '@', $out); @@ -509,8 +509,8 @@ EOD; $out = $fixture->four($sql); $expected = <<invoke\(\) --- line \d+ of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->one\(\) +-- line \d+ of /public/lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to ReflectionMethod->invoke\(\) +-- line \d+ of /public/lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->one\(\) EOD; $this->assertMatchesRegularExpression('@' . $this->unix_to_os_dirsep($expected) . '@', $out); @@ -518,11 +518,11 @@ EOD; $out = $fixture->four($sql); $expected = <<invoke\(\) --- line \d+ of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->one\(\) --- line \d+ of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->two\(\) --- line \d+ of /lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->three\(\) --- line \d+ of /lib/dml/tests/dml_test.php: call to test_dml_sql_debugging_fixture->four\(\) +-- line \d+ of /public/lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to ReflectionMethod->invoke\(\) +-- line \d+ of /public/lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->one\(\) +-- line \d+ of /public/lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->two\(\) +-- line \d+ of /public/lib/dml/tests/fixtures/test_dml_sql_debugging_fixture.php: call to test_dml_sql_debugging_fixture->three\(\) +-- line \d+ of /public/lib/dml/tests/dml_test.php: call to test_dml_sql_debugging_fixture->four\(\) EOD; $this->assertMatchesRegularExpression('@' . $this->unix_to_os_dirsep($expected) . '@', $out); diff --git a/public/lib/setuplib.php b/public/lib/setuplib.php index 3f191ebbe4d..24147907daa 100644 --- a/public/lib/setuplib.php +++ b/public/lib/setuplib.php @@ -471,8 +471,8 @@ function get_docs_url($path = null) { * @return string formatted backtrace, ready for output. */ function format_backtrace($callers, $plaintext = false) { - // do not use $CFG->dirroot because it might not be available in destructors - $dirroot = dirname(__DIR__); + // Do not use $CFG->dirroot because it might not be available in destructors. + $dirroot = dirname(__DIR__, 2); if (empty($callers)) { return ''; @@ -481,21 +481,25 @@ function format_backtrace($callers, $plaintext = false) { $from = $plaintext ? '' : '
    '; foreach ($callers as $caller) { if (!isset($caller['line'])) { - $caller['line'] = '?'; // probably call_user_func() + $caller['line'] = '?'; // Probably call_user_func(). } if (!isset($caller['file'])) { - $caller['file'] = 'unknownfile'; // probably call_user_func() + $caller['file'] = 'unknownfile'; // Probably call_user_func(). } $line = $plaintext ? '* ' : '
  • '; - $line .= 'line ' . $caller['line'] . ' of ' . str_replace($dirroot, '', $caller['file']); + $line .= sprintf( + 'line %d of %s', + $caller['line'], + str_replace($dirroot, '', $caller['file']), + ); if (isset($caller['function'])) { $line .= ': call to '; if (isset($caller['class'])) { $line .= $caller['class'] . $caller['type']; } - $line .= $caller['function'] . '()'; + $line .= "{$caller['function']}()"; } else if (isset($caller['exception'])) { - $line .= ': '.$caller['exception'].' thrown'; + $line .= ": {$caller['exception']} thrown"; } // Remove any non printable chars. diff --git a/public/lib/tests/setuplib_test.php b/public/lib/tests/setuplib_test.php index 0d388efbde5..38d820565f9 100644 --- a/public/lib/tests/setuplib_test.php +++ b/public/lib/tests/setuplib_test.php @@ -24,8 +24,8 @@ namespace core; * @copyright 2012 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +#[\PHPUnit\Framework\Attributes\CoversFunction('format_backtrace')] final class setuplib_test extends \advanced_testcase { - /** * Test get_docs_url_standard in the normal case when we should link to Moodle docs. */ @@ -540,4 +540,23 @@ final class setuplib_test extends \advanced_testcase { $this->expectNotToPerformAssertions(); require_phpunit_isolation(); } + + /** + * Ensure that formatted backtraces do not contain a full path but do contain the public dir. + */ + public function test_format_backtrace(): void { + // Note: Stack traces do not include the path for the location that they are generated. + // Use an IIFE to add an extra frame to the trace. + $backtrace = (fn (): array => debug_backtrace())(); + $output = format_backtrace($backtrace, true); + + // It should not contain the full dir. + $this->assertStringNotContainsString(__DIR__, $output); + + // But it should contain the local variant. + $this->assertStringContainsString(' of /public/lib/tests/setuplib_test.php', $output); + + // And a vendor path. + $this->assertStringContainsString(' of /vendor/', $output); + } }