PluginProbe ʕ •ᴥ•ʔ
JetBackup – Backup, Restore & Migrate / trunk
JetBackup – Backup, Restore & Migrate vtrunk
3.1.22.3 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.8.1 1.4.9 1.5.0 1.5.1 1.5.1.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.10 1.6.11 1.6.12 1.6.13 1.6.15 1.6.5.1 1.6.8.8 1.6.9 1.6.9.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7.5 2.0.8.7 2.0.9.11 2.0.9.14 2.0.9.15 2.0.9.6 2.0.9.7 2.0.9.9 3.1.10.7 3.1.11.1 3.1.12.3 3.1.13.4 3.1.14.17 3.1.15.4 3.1.16.1 3.1.17.5 3.1.18.10 3.1.18.8 3.1.18.9 3.1.19.8 3.1.20.3 3.1.21.3 3.1.7.9 3.1.9.2 trunk 1.1.90 1.1.91 1.2.0 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2
backup / src / JetBackup / Destination / Vendors / Box / DirIterator.php
backup / src / JetBackup / Destination / Vendors / Box Last commit date
Client 1 year ago .htaccess 1 year ago Box.php 1 year ago Cache.php 1 year ago ChunkedDownload.php 1 year ago ChunkedUpload.php 1 year ago DirIterator.php 1 year ago index.html 1 year ago web.config 1 year ago
DirIterator.php
194 lines
1 <?php
2 /*
3 *
4 * JetBackup @ package
5 * Created By Idan Ben-Ezra
6 *
7 * Copyrights @ JetApps
8 * https://www.jetapps.com
9 *
10 **/
11 namespace JetBackup\Destination\Vendors\Box;
12
13 use Exception;
14 use JetBackup\Destination\DestinationFile;
15 use JetBackup\Destination\Integration\DestinationDirIterator;
16 use JetBackup\Destination\Integration\DestinationFile as iDestinationFile;
17 use JetBackup\Destination\Vendors\Box\Client\Client;
18 use JetBackup\Destination\Vendors\Box\Client\ClientException;
19 use JetBackup\Destination\Vendors\Box\Client\File;
20 use JetBackup\Destination\Vendors\Box\Client\ListFiles;
21 use JetBackup\Exception\IOException;
22 use JetBackup\Exception\JBException;
23
24 defined( '__JETBACKUP__' ) or die( 'Restricted access' );
25
26 class DirIterator implements DestinationDirIterator {
27
28 const CHUNK_LIMIT = 1000;
29
30 private Box $_destination;
31 private string $_directory;
32 private ?ListFiles $_list=null;
33 private ?string $_parent_id=null;
34 /** @var File[] */
35 private array $_files=[];
36 private array $_subdirectories = []; // Queue of subdirectories
37 private array $_processedDirectories = [];
38
39 /**
40 * @param Box $destination
41 * @param string $directory
42 * @param string|null $parent_id
43 *
44 * @throws IOException
45 * @throws JBException
46 */
47 public function __construct(Box $destination, string $directory, ?string $parent_id=null) {
48
49 $this->_destination = $destination;
50 $this->_directory = $directory;
51 if(!$this->_destination->getClient()) throw new IOException("Unable to retrieve Box service");
52 $path = $this->_destination->getRealPath($this->_directory);
53
54 if(!$parent_id) $parent_id = $this->_destination->getFolderId($path == '/' ? '' : $this->_directory);
55 if($parent_id === null) return;
56 $this->_parent_id = $parent_id;
57 $this->_destination->getLogController()->logDebug("[DestinationDirIterator] Processing directory: {$this->_directory} with Parent ID: {$this->_parent_id}");
58
59 $this->rewind();
60 }
61
62 /**
63 * @param bool $rewind
64 *
65 * @return void
66 * @throws IOException
67 */
68 private function _loadChunk(bool $rewind = false): void {
69 $this->_destination->getLogController()->logDebug("[_loadChunk] Processing directory: {$this->_directory} with Parent ID: {$this->_parent_id}");
70 if (in_array($this->_directory, $this->_processedDirectories, true)) {
71 $this->_destination->getLogController()->logDebug("[_loadChunk] Skipping already processed directory: {$this->_directory}");
72 return;
73 }
74 $this->_processedDirectories[] = $this->_directory;
75
76 try {
77 $marker = '';
78 if (!$rewind && $this->_list) {
79 $marker = $this->_list->getNextPageToken();
80 if (!$marker) {
81 $this->_list = null;
82 return;
83 }
84 }
85
86 $this->_list = $this->_destination->_retries(function () use ($marker) {
87 return $this->_destination->getClient()->listFolder(
88 $this->_parent_id ?? Client::ROOT_FOLDER,
89 self::CHUNK_LIMIT,
90 $marker
91 );
92 }, "Failed fetching list of files");
93
94 $this->_files = $this->_list ? $this->_list->getFiles() : [];
95 foreach ($this->_files as $file) {
96 if ($file->getMimeType() === Client::MIMITYPE_DIR) {
97 $subdirPath = rtrim($this->_directory, '/') . '/' . $file->getName();
98 $subdirPath = preg_replace('#/+#', '/', $subdirPath); // Normalize slashes
99 $this->_subdirectories[] = [
100 'path' => $subdirPath,
101 'id' => $file->getId(),
102 ];
103 $this->_destination->getLogController()->logDebug("[_loadChunk] Enqueued subdirectory: $subdirPath with ID: {$file->getId()}");
104 }
105 }
106 } catch (Exception $e) {
107 if ($e->getCode() === 404) {
108 $this->_files = [];
109 } else {
110 throw new IOException($e->getMessage(), $e->getCode(), $e);
111 }
112 }
113 }
114
115
116
117 /**
118 * @return void
119 * @throws IOException
120 */
121 public function rewind(): void {
122 $this->_destination->getLogController()->logDebug("[rewind] Reloading current directory: {$this->_directory}");
123 $this->_loadChunk(true);
124 }
125
126
127 /**
128 * @return bool
129 * @throws IOException
130 */
131 public function hasNext(): bool {
132 if (!($this->_files && count($this->_files))) {
133 // If there are no more files in the current directory, move to the next subdirectory
134 if (!empty($this->_subdirectories)) {
135 $nextSubdir = array_shift($this->_subdirectories);
136 $this->_directory = $nextSubdir['path'];
137 $this->_parent_id = $nextSubdir['id'];
138
139 // Debugging: Log subdirectory switch
140 $this->_destination->getLogController()->logDebug("[hasNext] Switching to subdirectory: {$this->_directory} with ID: {$this->_parent_id}");
141
142 // Reload chunk for the new subdirectory
143 $this->rewind();
144
145 return $this->hasNext(); // Reevaluate after switching
146 }
147
148 return false; // No more files or subdirectories
149 }
150
151 return true;
152 }
153
154
155
156
157 /**
158 * @return ?iDestinationFile
159 * @throws IOException
160 */
161 public function getNext(): ?iDestinationFile {
162 if (!$this->hasNext()) return null;
163
164 // If `_files` is empty but there are subdirectories, `hasNext` handles traversal
165 if (empty($this->_files)) {
166 return null; // Safeguard against infinite loop
167 }
168
169 $nextFile = array_shift($this->_files);
170
171 $path = rtrim($this->_directory, '/') . '/' . $nextFile->getName();
172 $path = preg_replace('#/+#', '/', $path); // Normalize slashes
173 $basename = basename($path);
174
175 $file = new DestinationFile();
176 $file->setType($nextFile->getMimeType() === Client::MIMITYPE_DIR
177 ? iDestinationFile::TYPE_DIRECTORY
178 : iDestinationFile::TYPE_FILE);
179 $file->setName($basename);
180 $file->setPath($basename === $path ? '' : dirname($path));
181 $file->setSize($nextFile->getMimeType() === Client::MIMITYPE_DIR
182 ? ($nextFile->getSize() ?: 4096)
183 : $nextFile->getSize());
184 $file->setModifyTime($nextFile->getModificationTime());
185 $file->setFileData(json_encode(['id' => $nextFile->getId()]));
186
187 // Debugging: Log the file being processed
188 $this->_destination->getLogController()->logDebug("[getNext] Processing file: $basename with path: $path");
189
190 return $file;
191 }
192
193
194 }