Skip to content

Commit 2f260e4

Browse files
committed
Add moveFileOrDir method to handle cross-device file and directory moves
1 parent 9182cf1 commit 2f260e4

2 files changed

Lines changed: 69 additions & 0 deletions

File tree

src/Package/Artifact/imagick.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Package\Artifact;
6+
7+
use StaticPHP\Attribute\Artifact\AfterSourceExtract;
8+
use StaticPHP\Attribute\PatchDescription;
9+
use StaticPHP\Util\SourcePatcher;
10+
11+
class imagick
12+
{
13+
#[AfterSourceExtract('ext-imagick')]
14+
#[PatchDescription('Patch imagick for PHP 8.4 compatibility (versions < 3.8.0)')]
15+
public function patchImagickWith84(): void
16+
{
17+
// match imagick version id
18+
$file = SOURCE_PATH . '/php-src/ext/imagick/php_imagick.h';
19+
if (!file_exists($file)) {
20+
return;
21+
}
22+
$content = file_get_contents($file);
23+
if (preg_match('/#define PHP_IMAGICK_EXTNUM\s+(\d+)/', $content, $match) === 0) {
24+
return;
25+
}
26+
$extnum = intval($match[1]);
27+
if ($extnum < 30800) {
28+
SourcePatcher::patchFile('imagick_php84_before_30800.patch', SOURCE_PATH . '/php-src/ext/imagick');
29+
}
30+
}
31+
}

src/StaticPHP/Util/FileSystem.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,44 @@ public static function fullpath(string $path, string $relative_path_base): strin
493493
return FileSystem::convertPath($path);
494494
}
495495

496+
/**
497+
* Move file or directory, handling cross-device scenarios
498+
* Uses rename() if possible, falls back to copy+delete for cross-device moves
499+
*
500+
* @param string $source Source path
501+
* @param string $dest Destination path
502+
*/
503+
public static function moveFileOrDir(string $source, string $dest): void
504+
{
505+
$source = FileSystem::convertPath($source);
506+
$dest = FileSystem::convertPath($dest);
507+
508+
// Check if source and dest are on the same device to avoid cross-device rename errors
509+
$source_stat = @stat($source);
510+
$dest_parent = dirname($dest);
511+
$dest_stat = @stat($dest_parent);
512+
513+
// Only use rename if on same device
514+
if ($source_stat !== false && $dest_stat !== false && $source_stat['dev'] === $dest_stat['dev']) {
515+
if (@rename($source, $dest)) {
516+
return;
517+
}
518+
}
519+
520+
// Fall back to copy + delete for cross-device moves or if rename failed
521+
if (is_dir($source)) {
522+
FileSystem::copyDir($source, $dest);
523+
FileSystem::removeDir($source);
524+
} else {
525+
if (!copy($source, $dest)) {
526+
throw new FileSystemException("Failed to copy file from {$source} to {$dest}");
527+
}
528+
if (!unlink($source)) {
529+
throw new FileSystemException("Failed to remove source file: {$source}");
530+
}
531+
}
532+
}
533+
496534
private static function replaceFile(string $filename, int $replace_type = REPLACE_FILE_STR, mixed $callback_or_search = null, mixed $to_replace = null): false|int
497535
{
498536
logger()->debug('Replacing file with type[' . $replace_type . ']: ' . $filename);

0 commit comments

Comments
 (0)