Skip to content

Commit 5d2ed00

Browse files
author
Alex Damsted
committed
[#225] Fix Totara compatibility bug with the core get_directory_size function
1 parent c358a3e commit 5d2ed00

File tree

2 files changed

+159
-14
lines changed

2 files changed

+159
-14
lines changed

classes/check/dirsizes.php

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@
1717
/**
1818
* Dir sizes performance check.
1919
*
20-
* @package tool_heartbeat
21-
* @copyright 2023 Brendan Heywood <brendan@catalyst-au.net>
22-
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23-
*
20+
* @package tool_heartbeat
21+
* @copyright 2023 Brendan Heywood <brendan@catalyst-au.net>
22+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2423
*/
2524

2625
namespace tool_heartbeat\check;
@@ -30,9 +29,9 @@
3029
/**
3130
* Dir sizes performance check.
3231
*
33-
* @copyright 2023
34-
* @author Brendan Heywood <brendan@catalyst-au.net>
35-
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32+
* @copyright 2023
33+
* @author Brendan Heywood <brendan@catalyst-au.net>
34+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3635
*/
3736
class dirsizes extends check {
3837

@@ -44,11 +43,10 @@ class dirsizes extends check {
4443
public function get_result(): result {
4544
global $CFG;
4645

47-
$sizedataroot = get_directory_size($CFG->dataroot);
46+
$sizedataroot = $this->dirsize('dataroot', true);
4847
$summary = $sizedataroot;
4948
$details = "Shared paths:<br>";
5049
$details .= '$CFG->dataroot = ' . display_size($sizedataroot);
51-
5250
$details .= $this->dirsize('themedir');
5351
$details .= $this->dirsize('tempdir');
5452
$details .= $this->dirsize('cachedir');
@@ -61,19 +59,72 @@ public function get_result(): result {
6159
return new result(result::INFO, $summary, $details);
6260
}
6361
/**
64-
* Get a paths sizet
65-
* @param string $cfg the path to check
66-
* @return string size for a path as html
62+
* Get a path's size
63+
*
64+
* @param string $cfg the path to check
65+
* @param bool $rawsize return rawsize of directory
66+
* @return string $size for a path as html
6767
*/
68-
private function dirsize(string $cfg) {
68+
private function dirsize(string $cfg, bool $rawsize = false) {
6969
global $CFG;
7070
if (!property_exists($CFG, $cfg)) {
7171
return "<br>\$CFG->$cfg not in use";
7272
}
7373
$path = $CFG->{$cfg};
74-
$size = get_directory_size($path);
74+
75+
// If Totara, use Totara-compatible function.
76+
if (!empty($CFG->totara_version)) {
77+
$size = $this->get_directory_size_totara($path);
78+
} else {
79+
$size = get_directory_size($path);
80+
}
81+
82+
if ($rawsize) {
83+
return $size;
84+
}
7585

7686
return "<br>\$CFG->{$cfg} = " . display_size($size);
7787
}
7888

89+
/**
90+
* Recursively calculate the size of a directory (Totara-compatible).
91+
*
92+
* This replicates Moodle core's get_directory_size() logic,
93+
* but avoids using it directly for Totara compatibility.
94+
*
95+
* @param string $dir The directory path to measure
96+
* @return int Total size in bytes
97+
*/
98+
private function get_directory_size_totara(string $dir): int {
99+
if (!is_dir($dir)) {
100+
return 0;
101+
}
102+
103+
$size = 0;
104+
if (!$dh = @opendir($dir)) {
105+
return 0;
106+
}
107+
108+
while (false !== ($file = readdir($dh))) {
109+
// Skip hidden files and CVS dirs.
110+
if ($file[0] === '.' || $file === 'CVS') {
111+
continue;
112+
}
113+
114+
$fullfile = $dir . '/' . $file;
115+
116+
if (is_dir($fullfile)) {
117+
// Recurse into subdirectory.
118+
$size += $this->get_directory_size_totara($fullfile);
119+
} else {
120+
$filesize = filesize($fullfile);
121+
if ($filesize !== false) {
122+
$size += $filesize;
123+
}
124+
}
125+
}
126+
127+
closedir($dh);
128+
return $size;
129+
}
79130
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
// This file is part of Moodle - http://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
17+
namespace tool_heartbeat\check;
18+
19+
use tool_heartbeat\check\dirsizes;
20+
21+
/**
22+
* Test class for Totara tool_heartbeat\check\dirsizes
23+
*
24+
* @package tool_heartbeat
25+
* @author Alex Damsted <alexdamsted@catalyst-au.net>
26+
* @copyright 2025, Catalyst IT
27+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28+
*
29+
* @coversDefaultClass \tool_heartbeat\check\dirsizes
30+
*/
31+
final class dirsizes_totara_test extends \advanced_testcase {
32+
33+
protected function setUp(): void {
34+
parent::setUp();
35+
$this->resetAfterTest(true);
36+
}
37+
38+
/**
39+
* Ensure get_directory_size_totara counts file sizes correctly.
40+
*
41+
* @covers ::get_directory_size_totara
42+
*/
43+
public function test_dirsize_totara_counts_files(): void {
44+
global $CFG;
45+
$dir = make_request_directory('tool_heartbeat_test1');
46+
$CFG->tempdir = $dir;
47+
48+
// Create two test files with known sizes.
49+
file_put_contents($dir . '/file1.txt', str_repeat('a', 100));
50+
file_put_contents($dir . '/file2.txt', str_repeat('b', 200));
51+
$check = new dirsizes();
52+
$size = $this->invokeMethod($check, 'get_directory_size_totara', [$CFG->tempdir]);
53+
54+
$this->assertEquals(300, $size);
55+
}
56+
57+
/**
58+
* Ensure get_directory_size_totara counts nested files in subdirectories.
59+
*
60+
* @covers ::get_directory_size_totara
61+
*/
62+
public function test_dirsize_totara_counts_nested_files(): void {
63+
global $CFG;
64+
65+
$dir = make_request_directory('tool_heartbeat_test2');
66+
$CFG->tempdir = $dir;
67+
68+
// Create subdirectory with file.
69+
$subdir = $dir . '/subdir';
70+
check_dir_exists($subdir);
71+
file_put_contents($subdir . '/nested.txt', str_repeat('x', 150));
72+
73+
$check = new dirsizes();
74+
$size = $this->invokeMethod($check, 'get_directory_size_totara', [$CFG->tempdir]);
75+
76+
$this->assertGreaterThanOrEqual(150, $size);
77+
}
78+
79+
/**
80+
* Call private methods.
81+
*
82+
* @param object $object
83+
* @param string $method
84+
* @param array $args
85+
* @return mixed
86+
*/
87+
protected function invokemethod($object, string $method, array $args = []) {
88+
$reflection = new \ReflectionClass(get_class($object));
89+
$method = $reflection->getMethod($method);
90+
$method->setAccessible(true);
91+
return $method->invokeArgs($object, $args);
92+
}
93+
}
94+

0 commit comments

Comments
 (0)