Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2.3.0 #43

Merged
merged 34 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
750798a
Add PHP 8.3 to test matrix
ewilan-riviere Mar 7, 2024
83642ea
Update README.md with warning and note messages
ewilan-riviere Mar 7, 2024
e91eac2
add docs for password archives
ewilan-riviere Mar 7, 2024
07d9014
docs
ewilan-riviere Mar 7, 2024
13200b9
add readFromString
ewilan-riviere Mar 7, 2024
6e3c537
Refactor method name in ArchiveZipCreate class
ewilan-riviere Mar 7, 2024
4823226
Add password parameter to SevenZipProcess::make() method
ewilan-riviere Mar 7, 2024
8b1b87a
Update RarArchive::open() method to include password parameter
ewilan-riviere Mar 7, 2024
11c7852
Add password functionality and improve file retrieval in BaseArchive
ewilan-riviere Mar 7, 2024
4b515ae
Update SevenZipProcess instantiation to include password
ewilan-riviere Mar 7, 2024
ee692f2
Add password protection for zip archives
ewilan-riviere Mar 7, 2024
b61a361
Update method name in ArchiveCreateTest.php
ewilan-riviere Mar 7, 2024
e211311
Refactor ArchiveTest.php to use getFileItems() instead of getFiles()
ewilan-riviere Mar 7, 2024
e4d80e1
Refactor getFileItems method in EbookTest.php
ewilan-riviere Mar 7, 2024
a3278e2
Refactor getFileItems method in PdfTest.php
ewilan-riviere Mar 7, 2024
ae5fbbe
Add archive password files
ewilan-riviere Mar 7, 2024
b94ded7
Fix styling
ewilan-riviere Mar 7, 2024
490bcfd
Update macOS workflow to install dependencies
ewilan-riviere Mar 7, 2024
a7c249a
Merge branch 'develop' of github.com:kiwilan/php-archive into develop
ewilan-riviere Mar 7, 2024
e1fcb6c
Add PHP setup and update dependencies in run-macos.yml
ewilan-riviere Mar 7, 2024
782b105
Add methods for reading archives from string and handling password-pr…
ewilan-riviere Mar 7, 2024
978d11b
Add password parameter to read methods in Archive class
ewilan-riviere Mar 7, 2024
fe88a9a
Add binaryPath parameter to SevenZipProcess constructor
ewilan-riviere Mar 7, 2024
a536e94
Add support for password-protected PDF files in ArchivePdf::read() me…
ewilan-riviere Mar 7, 2024
5dab483
Refactor ArchivePhar::read() method to support password-protected arc…
ewilan-riviere Mar 7, 2024
487518b
Add optional password parameter to ArchiveRar::read() method
ewilan-riviere Mar 7, 2024
16deaaa
Update ArchiveSevenZip class to support password-protected archives
ewilan-riviere Mar 7, 2024
091a943
Add support for password in ArchiveZip class and override binary path
ewilan-riviere Mar 7, 2024
3f672f1
Remove unnecessary debug statement
ewilan-riviere Mar 7, 2024
b794c9f
Merge branch 'main' into develop
ewilan-riviere Mar 7, 2024
630c47f
Update dependencies and add 7zip installation for Windows workflow
ewilan-riviere Mar 7, 2024
074e91e
Update version number in composer.json
ewilan-riviere Mar 7, 2024
d0eb8ee
Update binary path for handling archives
ewilan-riviere Mar 7, 2024
3c29174
Update version in composer.json
ewilan-riviere Mar 7, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/run-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ jobs:
runs-on: macos-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install dependencies
continue-on-error: true
run: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew update
brew install p7zip
brew install rar
brew install ghostscript
brew install imagemagick
shell: bash

- name: Checkout code
uses: actions/checkout@v4

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest]
php: [8.1, 8.2]
php: [8.1, 8.2, 8.3]
stability: [prefer-stable]

name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/run-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ jobs:
iex "& {$(irm get.scoop.sh)} -RunAsAdmin"
scoop update
scoop install 7zip
scoop install which
scoop checkup
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
which 7z
shell: powershell

- name: Checkout code
Expand Down
44 changes: 39 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ PHP package to handle archives (`.zip`, `.rar`, `.tar`, `.7z`, `.pdf`) with unif

Supports Linux, macOS and Windows.

> **Warning**
> [!WARNING]
>
> For some formats (`.rar` and `.7z`) [`rar` PHP extension](https://github.com/cataphract/php-rar) or [p7zip](https://www.7-zip.org/) binary could be necessary, see [Requirements](#requirements).

Expand All @@ -35,15 +35,15 @@ Supports Linux, macOS and Windows.
| `.7z`, `.cb7` | ✅ | [`p7zip`](https://www.7-zip.org/) binary | `p7zip` binary |
| `.pdf` | ✅ | Optional (for extraction) [`imagick` PHP extension](https://github.com/Imagick/imagick) | `smalot/pdfparser` |

> **Note**
> [!NOTE]
>
> Here you can read some installation guides for dependencies
>
> - [`p7zip` guide](https://gist.github.com/ewilan-riviere/85d657f9283fa6af255531d97da5d71d)
> - [`rar` PHP extension guide](https://gist.github.com/ewilan-riviere/3f4efd752905abe24fd1cd44412d9db9#winrar)
> - [`imagick` PHP extension guide](https://gist.github.com/ewilan-riviere/3f4efd752905abe24fd1cd44412d9db9#imagemagick)

> **Warning**
> [!WARNING]
>
> - **On macOS**, for `.rar` extract, you have to [install `rar` binary](https://gist.github.com/ewilan-riviere/85d657f9283fa6af255531d97da5d71d#macos) to extract files, `p7zip` not support `.rar` extraction.
> - **On Windows**, for `.pdf` extract, [`imagick` PHP extension](https://github.com/Imagick/imagick) have to work but **my tests failed on this feature**. So to extract PDF pages I advice to use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install).
Expand All @@ -53,7 +53,7 @@ If you want more information, you can read section [**About**](#about).
## Features

- List files as `ArchiveItem` array
- With `getFiles()` method: list of files
- With `getFileItems()` method: list of files
- With `getFirst()` method: first file
- With `getLast()` method: last file
- With `find()` method: find first file that match with `path` property
Expand Down Expand Up @@ -91,7 +91,7 @@ With archive file (`.zip`, `.rar`, `.tar`, `.7z`, `epub`, `cbz`, `cbr`, `cb7`, `
```php
$archive = Archive::read('path/to/archive.zip');

$files = $archive->getFiles(); // ArchiveItem[]
$files = $archive->getFileItems(); // ArchiveItem[]
$count = $archive->getCount(); // int of files count

$images = $archive->filter('jpeg'); // ArchiveItem[] with `jpeg` in their path
Expand All @@ -113,6 +113,40 @@ $content = $archive->getContents($archive->getFirst()); // PDF page as image
$text = $archive->getText($archive->getFirst()); // PDF page as text
```

### Read from string

You can read archive from string with `readFromString` method.

```php
$archive = Archive::readFromString($string);
```

This method will try to detect the format of the archive from the string. If you have an error, you can use `readFromString` method with third argument to specify the format of the archive.

```php
$archive = Archive::readFromString($string, extension: 'zip');
```

### Password protected

You can read password protected archives with `read` or `readFromString` method.

> [!WARNING]
>
> Works only with archives and not with PDF files.

```php
$archive = Archive::read('path/to/password-protected-archive.zip', 'password');
```

### Override binary path

For `p7zip` binary, you can override the path with `overrideBinaryPath` method.

```php
$archive = Archive::read($path)->overrideBinaryPath('/opt/homebrew/bin/7z');
```

### Stat

From `stat` PHP function: <https://www.php.net/manual/en/function.stat.php>
Expand Down
55 changes: 50 additions & 5 deletions src/Archive.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ protected function __construct(
protected string $path,
protected string $extension,
protected ArchiveEnum $type,
protected ?string $password = null,
) {
}

/**
* Read an archive from the path.
*/
public static function read(string $path): BaseArchive
public static function read(string $path, ?string $password = null): BaseArchive
{
if (! file_exists($path)) {
throw new \Exception("File {$path} not found");
Expand All @@ -31,7 +32,7 @@ public static function read(string $path): BaseArchive
$mimeType = Archive::getMimeType($path);
$extension = pathinfo($path, PATHINFO_EXTENSION);
$type = ArchiveEnum::fromExtension($extension, $mimeType);
$self = new self($path, $extension, $type);
$self = new self($path, $extension, $type, $password);

/** @var BaseArchive */
$archive = match ($self->type) {
Expand All @@ -42,14 +43,58 @@ public static function read(string $path): BaseArchive
ArchiveEnum::pdf => ArchivePdf::class,
};

return $archive::read($self->path);
return $archive::read($self->path, $self->password);
}

/**
* Create an archive from contents.
*
* @param string $contents Contents of the archive.
* @param string|null $password Password of the archive, can be null if no password.
* @param string|null $extension Extension of the archive, can be null to detect automatically mimetype.
*/
public static function readFromString(string $contents, ?string $password = null, ?string $extension = null): BaseArchive
{
$file_info = new \finfo(FILEINFO_MIME_TYPE);
$mime_type = $file_info->buffer($contents);

$extension = match ($mime_type) {
'application/x-bzip2' => 'bz2',
'application/gzip' => 'gz',
'application/x-rar' => 'rar',
'application/epub+zip' => 'epub',
'application/pdf' => 'pdf',
'application/zip' => 'zip',
'application/x-tar' => 'tar',
'application/x-7z-compressed' => '7z',
'application/x-cbr' => 'rar',
'application/x-cbz' => 'cbz',
'application/x-cbt' => 'tar',
'application/x-cb7' => '7z',
default => null,
};

if ($extension === null) {
throw new \Exception('Archive: Error detecting extension from mime type, please add manually archive extension as third parameter of `readFromString()`.');
}

$path = tempnam(sys_get_temp_dir(), 'archive_');
rename($path, $path .= ".{$extension}"); // Rename to add extension

try {
file_put_contents($path, $contents);
} catch (\Throwable $th) {
throw new \Exception('Archive: Error creating temporary file with `readFromString()`.');
}

return self::read($path, $password);
}

/**
* Create an archive from path, allowing extensions are `zip`, `epub`, `cbz`.
*
* @param string $path Path to the archive
* @param bool $skipAllowed Skip allowed extensions check
* @param string $path Path to the archive
* @param bool $skipAllowed Skip allowed extensions check
*
* @throws \Exception
*/
Expand Down
18 changes: 9 additions & 9 deletions src/ArchiveZipCreate.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ protected function __construct(
/**
* Create a new instance of ArchiveZipCreate, allowing extensions are `zip`, `epub`, `cbz`.
*
* @param string $path Path to the archive
* @param bool $skipAllowed Skip allowed extensions check
* @param string $path Path to the archive
* @param bool $skipAllowed Skip allowed extensions check
*
* @throws \Exception
*/
Expand Down Expand Up @@ -64,16 +64,16 @@ public function getCount(): int
/**
* @return ArchiveFile[]
*/
public function getFiles(): array
public function getFileItems(): array
{
return $this->files;
}

/**
* Add a new file to the archive from existing file.
*
* @param string $outputPath Path to the file inside the archive
* @param string $pathToFile Path to the file to add
* @param string $outputPath Path to the file inside the archive
* @param string $pathToFile Path to the file to add
*/
public function addFile(string $outputPath, string $pathToFile): self
{
Expand All @@ -86,8 +86,8 @@ public function addFile(string $outputPath, string $pathToFile): self
/**
* Add a new file to the archive from string.
*
* @param string $outputPath Path to the file inside the archive
* @param string $content Content of the file to add
* @param string $outputPath Path to the file inside the archive
* @param string $content Content of the file to add
*/
public function addFromString(string $outputPath, string $content): self
{
Expand All @@ -100,8 +100,8 @@ public function addFromString(string $outputPath, string $content): self
/**
* Add a full directory to the archive, including subdirectories.
*
* @param string $relativeTo Relative path to the directory inside the archive
* @param string $path Path to the directory to add
* @param string $relativeTo Relative path to the directory inside the archive
* @param string $path Path to the directory to add
*
* ```php
* $archive->addDirectory('./to/directory', '/path/to/directory');
Expand Down
14 changes: 12 additions & 2 deletions src/Processes/SevenZipProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@ class SevenZipProcess

protected function __construct(
protected string $path,
protected ?string $password = null,
protected ?string $binaryPath = null,
) {
}

public static function make(string $path): self
public static function make(string $path, ?string $password = null, ?string $binaryPath = null): self
{
if (! file_exists($path)) {
throw new Exception("File does not exist: {$path}");
}

$self = new self($path);
$self = new self($path, $password, $binaryPath);
$temp = ArchiveTemporaryDirectory::make();
$self->outputDir = $temp->path();
$self->isDarwin = PHP_OS_FAMILY === 'Darwin';
Expand Down Expand Up @@ -75,6 +77,14 @@ public function execute(string $command, array $args): array
{
SevenZipProcess::test();

if ($this->password) {
$args = ['-p'.$this->password, ...$args];
}

if ($this->binaryPath) {

}

$command = "{$command} ".implode(' ', $args);

// $process = new Process([$command, ...$args]);
Expand Down
5 changes: 4 additions & 1 deletion src/Readers/ArchivePdf.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ class ArchivePdf extends BaseArchive
{
protected string $pdfExt = 'jpg';

public static function read(string $path): self
public static function read(string $path, ?string $password = null): self
{
$self = new self();
if ($password) {
$self->password = $password;
}
$self->setup($path);
$self->parse();

Expand Down
9 changes: 8 additions & 1 deletion src/Readers/ArchivePhar.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@

class ArchivePhar extends BaseArchive
{
public static function read(string $path): self
public static function read(string $path, ?string $password = null): BaseArchive
{
$self = new self();
if ($password) {
$self->password = $password;
$self = ArchiveSevenZip::read($path, $password);

return $self;
}

$self->setup($path);
$self->parse();

Expand Down
11 changes: 7 additions & 4 deletions src/Readers/ArchiveRar.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@

class ArchiveRar extends BaseArchive
{
public static function read(string $path): BaseArchive
public static function read(string $path, ?string $password = null): BaseArchive
{
$self = new self();
if ($password) {
$self->password = $password;
}

if (! BaseArchive::extensionRarTest(false)) {
BaseArchive::binaryP7zipTest();

$self = ArchiveSevenZip::read($path);
$self = ArchiveSevenZip::read($path, $password);

return $self;
}
Expand Down Expand Up @@ -91,7 +94,7 @@ private function parse(): static
{
$this->extensionRarTest();

$archive = RarArchive::open($this->path);
$archive = RarArchive::open($this->path, $this->password);
$this->stat = ArchiveStat::make($this->path);
$this->stat->setComment($archive->getComment());
$archive->close();
Expand All @@ -111,7 +114,7 @@ private function parse(): static
*/
private function parser(Closure $closure): mixed
{
$archive = RarArchive::open($this->getPath());
$archive = RarArchive::open($this->getPath(), $this->password);

if ($archive->isBroken()) {
throw new \Exception("Archive is broken {$this->getPath()}");
Expand Down
Loading
Loading