Skip to content

Commit f1fac92

Browse files
committed
feat: support sort in TorrentFile::getFileTree
closed: #16, #17
1 parent ab58de2 commit f1fac92

File tree

5 files changed

+159
-4
lines changed

5 files changed

+159
-4
lines changed

README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,39 @@ $torrent->parse(); // ['total_size' => $totalSize, 'count' => $count, 'files' =
188188
*/
189189
$size = $torrent->getSize();
190190
$count = $torrent->getFileCount();
191+
192+
/**
193+
* Return a list like:
194+
* [
195+
* ["path" => "filename1", "size" => 123], // 123 is file size
196+
* ["path" => "directory/filename2", "size" => 2345]
197+
* ]
198+
*
199+
*/
191200
$fileList = $torrent->getFileList();
192-
$fileTree = $torrent->getFileTree();
201+
202+
203+
/**
204+
* Return a dict like:
205+
* [
206+
* "torrentname" => [
207+
* "directory" => [
208+
* "filename2" => 2345
209+
* ],
210+
* "filename1" => 123
211+
* ]
212+
* ]
213+
*
214+
* > Add in v2.4.0
215+
* You can now pass argument to control the fileTree sort type. By default, this argument is TorrentFile::FILETREE_SORT_NORMAL.
216+
* Control Const (see different in `tests/TorrentFileTreeSortTest.php` file):
217+
* - TorrentFile::FILETREE_SORT_NORMAL : not sort, also means sort by torrent file parsed order
218+
* - TorrentFile::FILETREE_SORT_STRING : sort by filename ASC ("natural ordering" and "case-insensitively")
219+
* - TorrentFile::FILETREE_SORT_FOLDER : sort by filetype (first folder, then file)
220+
* - TorrentFile::FILETREE_SORT_NATURAL: sort by both filetype and filename ( same as `TorrentFile::FILETREE_SORT_NAME | TorrentFile::FILETREE_SORT_FOLDER` )
221+
*
222+
*/
223+
$fileTree = $torrent->getFileTree(?$sortType = self::FILETREE_SORT_NORMAL);
193224

194225
// 6. Other method
195226
$torrent->cleanCache();

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"php": "^7.3|^8.0"
1515
},
1616
"require-dev": {
17+
"ext-json": "*",
1718
"phpunit/phpunit": "^9.0"
1819
},
1920
"autoload": {

src/TorrentFile.php

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,20 @@ class TorrentFile
3232
public const PROTOCOL_V2 = 'v2'; // Version 2 of the BitTorrent protocol, using SHA-256 hashes
3333
public const PROTOCOL_HYBRID = 'hybrid'; // Torrent with both v1 and v2 support
3434

35+
// BitTorrent File Mode
3536
public const FILEMODE_SINGLE = 'single';
3637
public const FILEMODE_MULTI = 'multi';
3738

39+
// Control for ->getFileTree() output, by default it is normal as parsed order
40+
public const FILETREE_SORT_NORMAL = 0x00;
41+
public const FILETREE_SORT_STRING = 0x01;
42+
public const FILETREE_SORT_FOLDER = 0x10;
43+
public const FILETREE_SORT_NATURAL = 0x11; // same as self::FILETREE_SORT_NAME | self::FILETREE_SORT_FOLDER
44+
3845
// store torrent dict
3946
private $data;
4047

41-
// we may have some tmp data when parse, so we store here to avoid regenerate
48+
// we may have some tmp data when parsed, so we store here to avoid regenerate
4249
private $cache = [];
4350

4451
// Custom validator for parse torrent
@@ -693,6 +700,25 @@ public function getFileList()
693700
return $this->parse()['files'];
694701
}
695702

703+
private static function sortFileTreeRecursive(array &$fileTree, $sortByName = false, $sortByFolder = false): array
704+
{
705+
if ($sortByName) {
706+
ksort($fileTree, SORT_NATURAL | SORT_FLAG_CASE);
707+
}
708+
709+
$isoFile = [];
710+
foreach ($fileTree as $key => &$item) {
711+
if (is_array($item)) {
712+
$fileTree[$key] = self::sortFileTreeRecursive($item, $sortByName, $sortByFolder);
713+
} else if ($sortByFolder) {
714+
$isoFile[$key] = $item;
715+
unset($fileTree[$key]);
716+
}
717+
}
718+
$fileTree = array_merge($fileTree, $isoFile);
719+
return $fileTree;
720+
}
721+
696722
/**
697723
* Return a dict like:
698724
* [
@@ -704,9 +730,18 @@ public function getFileList()
704730
* ]
705731
* ]
706732
*/
707-
public function getFileTree()
733+
public function getFileTree($sortType = self::FILETREE_SORT_NORMAL)
708734
{
709-
return $this->parse()['fileTree'];
735+
$fileTree = $this->parse()['fileTree'];
736+
737+
$sortByName = ($sortType & self::FILETREE_SORT_STRING) === self::FILETREE_SORT_STRING;
738+
$sortByFolder = ($sortType & self::FILETREE_SORT_FOLDER) === self::FILETREE_SORT_FOLDER;
739+
740+
if ($sortByName || $sortByFolder) {
741+
self::sortFileTreeRecursive($fileTree, $sortByName, $sortByFolder);
742+
}
743+
744+
return $fileTree;
710745
}
711746

712747
public function cleanCache()

tests/TorrentFileTreeSortTest.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
4+
use Rhilip\Bencode\TorrentFile;
5+
use PHPUnit\Framework\TestCase;
6+
7+
class TorrentFileTreeSortTest extends TestCase
8+
{
9+
10+
protected function setUp(): void
11+
{
12+
$this->torrent = TorrentFile::load("tests/asserts/test_tree_sort.torrent");
13+
$this->torrent->parse();
14+
}
15+
16+
public function testGetFileTreeByParse()
17+
{
18+
$this->assertEquals(
19+
json_encode([
20+
'test_tree_sort' => [
21+
'file.txt' => 1048576,
22+
'z' => [
23+
'c.txt' => 1048576,
24+
],
25+
'a' => [
26+
'd.txt' => 1048576,
27+
'c.txt' => 1048576
28+
]
29+
]
30+
]),
31+
json_encode($this->torrent->getFileTree()));
32+
}
33+
34+
public function testGetFileTreeByString()
35+
{
36+
$this->assertEquals(
37+
json_encode([
38+
'test_tree_sort' => [
39+
'a' => [
40+
'c.txt' => 1048576,
41+
'd.txt' => 1048576
42+
],
43+
'file.txt' => 1048576,
44+
'z' => [
45+
'c.txt' => 1048576,
46+
],
47+
]
48+
]),
49+
json_encode($this->torrent->getFileTree(TorrentFile::FILETREE_SORT_STRING)));
50+
}
51+
52+
public function testGetFileTreeByFolder()
53+
{
54+
$this->assertEquals(
55+
json_encode([
56+
'test_tree_sort' => [
57+
'z' => [
58+
'c.txt' => 1048576,
59+
],
60+
'a' => [
61+
'd.txt' => 1048576,
62+
'c.txt' => 1048576
63+
],
64+
'file.txt' => 1048576
65+
]
66+
]),
67+
json_encode($this->torrent->getFileTree(TorrentFile::FILETREE_SORT_FOLDER)));
68+
}
69+
70+
public function testGetFileTreeByNatural()
71+
{
72+
$this->assertEquals(
73+
json_encode([
74+
'test_tree_sort' => [
75+
'a' => [
76+
'c.txt' => 1048576,
77+
'd.txt' => 1048576
78+
],
79+
'z' => [
80+
'c.txt' => 1048576,
81+
],
82+
'file.txt' => 1048576
83+
]
84+
]),
85+
json_encode($this->torrent->getFileTree(TorrentFile::FILETREE_SORT_NATURAL)));
86+
}
87+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
d4:infod5:filesld6:lengthi1048576e4:pathl8:file.txteed6:lengthi1048576e4:pathl1:z5:c.txteed6:lengthi1048576e4:pathl1:a5:d.txteed6:lengthi1048576e4:pathl1:a5:c.txteee4:name14:test_tree_sort12:piece lengthi524288e6:pieces160:��=1! ���$�ad�����g����f1|�5�6zV�{-SF0�Rk�\-�r���2j��[���9�l2UH�ox�|ASF0�Rk�\-�r���2j��[���9�l2UH�ox�|ASF0�Rk�\-�r���2j��[���9�l2UH�ox�|Aee

0 commit comments

Comments
 (0)