diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-15 00:58:38 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-15 19:51:17 +0900 |
| commit | 38a4e30dee2c8c806afc67b77363da88c0f21c49 (patch) | |
| tree | 8512bfa71e44b826e52caaa346658ab993fe16f3 /crates/shirabe | |
| parent | a9d321f731b13b6cdbd3f8069d6bd60558038edf (diff) | |
| download | php-shirabe-38a4e30dee2c8c806afc67b77363da88c0f21c49.tar.gz php-shirabe-38a4e30dee2c8c806afc67b77363da88c0f21c49.tar.zst php-shirabe-38a4e30dee2c8c806afc67b77363da88c0f21c49.zip | |
feat(port): port ZipArchiver.php
Diffstat (limited to 'crates/shirabe')
| -rw-r--r-- | crates/shirabe/src/package/archiver/archiver_interface.rs | 2 | ||||
| -rw-r--r-- | crates/shirabe/src/package/archiver/zip_archiver.rs | 97 |
2 files changed, 98 insertions, 1 deletions
diff --git a/crates/shirabe/src/package/archiver/archiver_interface.rs b/crates/shirabe/src/package/archiver/archiver_interface.rs index 79299af..82e976d 100644 --- a/crates/shirabe/src/package/archiver/archiver_interface.rs +++ b/crates/shirabe/src/package/archiver/archiver_interface.rs @@ -8,7 +8,7 @@ pub trait ArchiverInterface { format: String, excludes: Vec<String>, ignore_filters: bool, - ) -> String; + ) -> anyhow::Result<String>; fn supports(&self, format: String, source_type: Option<String>) -> bool; } diff --git a/crates/shirabe/src/package/archiver/zip_archiver.rs b/crates/shirabe/src/package/archiver/zip_archiver.rs index bd15193..ab7c284 100644 --- a/crates/shirabe/src/package/archiver/zip_archiver.rs +++ b/crates/shirabe/src/package/archiver/zip_archiver.rs @@ -1 +1,98 @@ //! ref: composer/src/Composer/Package/Archiver/ZipArchiver.php + +use indexmap::IndexMap; +use shirabe_php_shim::{class_exists, fileperms, method_exists, pack, realpath, PhpMixed, RuntimeException, ZipArchive}; +use crate::package::archiver::archivable_files_finder::ArchivableFilesFinder; +use crate::package::archiver::archiver_interface::ArchiverInterface; +use crate::util::filesystem::Filesystem; +use crate::util::platform::Platform; + +#[derive(Debug)] +pub struct ZipArchiver; + +impl ZipArchiver { + fn formats() -> IndexMap<String, bool> { + let mut map = IndexMap::new(); + map.insert("zip".to_string(), true); + map + } + + fn compression_available(&self) -> bool { + class_exists("ZipArchive") + } +} + +impl ArchiverInterface for ZipArchiver { + fn archive( + &self, + sources: String, + target: String, + format: String, + excludes: Vec<String>, + ignore_filters: bool, + ) -> anyhow::Result<String> { + let fs = Filesystem::new(); + let sources_realpath = realpath(&sources); + let sources = if let Some(p) = sources_realpath { + p + } else { + sources + }; + let sources = fs.normalize_path(&sources); + + let mut zip = ZipArchive::new(); + let res = zip.open(&target, ZipArchive::CREATE); + if res == true { + let files = ArchivableFilesFinder::new(&sources, excludes, ignore_filters)?; + for file in files { + let filepath = file.get_pathname(); + let mut relative_path = file.get_relative_pathname(); + + if Platform::is_windows() { + relative_path = shirabe_php_shim::strtr(&relative_path, "\\", "/"); + } + + if file.is_dir() { + zip.add_empty_dir(&relative_path); + } else { + zip.add_file(&filepath, &relative_path); + } + + // setExternalAttributesName() is only available with libzip 0.11.2 or above + if method_exists(&PhpMixed::Null, "setExternalAttributesName") { + let perms = fileperms(&filepath); + zip.set_external_attributes_name(&relative_path, ZipArchive::OPSYS_UNIX, perms << 16); + } + } + if zip.close() { + if !std::path::Path::new(&target).exists() { + // create minimal valid ZIP file (Empty Central Directory + End of Central Directory record) + let eocd = pack("VvvvvVVv", &[ + PhpMixed::Int(0x06054b50), // End of central directory signature + PhpMixed::Int(0), // Number of this disk + PhpMixed::Int(0), // Disk where central directory starts + PhpMixed::Int(0), // Number of central directory records on this disk + PhpMixed::Int(0), // Total number of central directory records + PhpMixed::Int(0), // Size of central directory (bytes) + PhpMixed::Int(0), // Offset of start of central directory + PhpMixed::Int(0), // Comment length + ]); + std::fs::write(&target, &eocd)?; + } + + return Ok(target); + } + } + let message = format!( + "Could not create archive '{}' from '{}': {}", + target, + sources, + zip.get_status_string() + ); + Err(RuntimeException { message, code: 0 }.into()) + } + + fn supports(&self, format: String, _source_type: Option<String>) -> bool { + Self::formats().contains_key(&format) && self.compression_available() + } +} |
