Skip to content

Commit

Permalink
Merge pull request #1 from drhino/dev
Browse files Browse the repository at this point in the history
Merges v1.1.0 into master
  • Loading branch information
drhino committed Jun 26, 2022
2 parents 5cb6215 + 26801ec commit 5b2fbde
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 51 deletions.
22 changes: 22 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.236.0/containers/ubuntu/.devcontainer/base.Dockerfile

# [Choice] Ubuntu version (use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon): ubuntu-22.04, ubuntu-20.04, ubuntu-18.04
# Release: 20.04
# Codename: focal
# Description: Ubuntu 20.04.4 LTS
ARG VARIANT="jammy"
FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT}

#
# PHP 7.4(.3)
#
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends php7.4 php7.4-zip

#
# PHP 8.0(.20)
#
#RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends software-properties-common \
# && add-apt-repository -y ppa:ondrej/php \
# && apt-get -y install --no-install-recommends php8.0 php8.0-zip
28 changes: 28 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.236.0/containers/ubuntu
{
"name": "Ubuntu",
"build": {
"dockerfile": "Dockerfile",
// Update 'VARIANT' to pick an Ubuntu version: jammy / ubuntu-22.04, focal / ubuntu-20.04, bionic /ubuntu-18.04
// Use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon.
"args": { "VARIANT": "ubuntu-20.04" }
},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "uname -a",
"postCreateCommand": "php -r \"copy('https://getcomposer.org/installer', 'composer-setup.php');\" && php -r \"if (hash_file('sha384', 'composer-setup.php') !== '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') unlink('composer-setup.php');\" && php composer-setup.php && php composer.phar install && rm composer-setup.php",

// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode",
"features": {
"git": "os-provided"
},
"extensions": [
"eserozvataf.one-dark-pro-monokai-darker",
"xdebug.php-debug"
]
}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
downloaded/
vendor/
*.lock
composer.phar
composer.lock
20 changes: 20 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch currently open script",
"type": "php",
"request": "launch",
"program": "${file}",
"cwd": "${fileDirname}",
"port": 0,
"runtimeArgs": [
"-dxdebug.start_with_request=yes"
],
"env": {
"XDEBUG_MODE": "debug,develop",
"XDEBUG_CONFIG": "client_port=${port}"
}
}
]
}
92 changes: 58 additions & 34 deletions GitDownload.php
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
<?php

use Curl\Curl;
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local;

/*
* Clone a Github repository with PHP & ZipArchive. Without using Git or exec()!
*/
class GitDownload
{
private $curl;
private $dir;
private $fs;

function __construct($dir)
/**
* Sets the output directory location and creates the Filesystem.
*
* @param String $dir Directory to store the contents into.
*/
public function __construct(String $dir)
{
$this->curl = new Curl;
$this->dir = $dir;
$this->fs = new Filesystem(new Local($dir));
}
$this->dir = $dir;

function __destruct()
{
$this->curl->close();
// Creates a Filesystem based on the Flysystem version.
$this->fs = new Filesystem(
class_exists('League\Flysystem\Adapter\Local')
// Version 1
? new League\Flysystem\Adapter\Local($dir)
// Version 3
: new League\Flysystem\Local\LocalFilesystemAdapter($dir)
);
}

/**
Expand All @@ -31,37 +35,57 @@ function __destruct()
* @param String $author Github author
* @param String $repo Github repository
* @param String $branch Repository branch
*
* @throws Exception ZipArchive failed
*
* @return String $absolute path to directory location.
*
* @see https://www.php.net/manual/en/function.fopen
* @see https://stackoverflow.com/a/2174899/19052212
* @see https://www.php.net/manual/en/function.stream-context-create.php
* @see https://www.php.net/manual/en/ziparchive.open.php
* @see https://www.php.net/manual/en/zip.constants.php#ziparchive.constants.rdonly
* @see https://www.php.net/manual/en/ziparchive.extractto.php
*/
public function clone($author, $repo, $branch)
public function clone(String $author, String $repo, String $branch): String
{
$contents = $this->get('https://codeload.github.com/' . $author . '/' . $repo . '/zip/' . $branch);
$absolute = $this->dir . '/' . $author . '/';

$this->fs->put($author . '/' . $repo . '.zip', $contents);
$url = 'https://codeload.github.com/' . $author . '/' . $repo . '/zip/' . $branch;

$relative = $author . '/' . $repo . '.zip';
$absolute = $this->dir . '/' . $author . '/' . $repo;
$resource = @fopen($url, 'rb');

// Downloads the zipfile to the local filesystem.
$this->fs->has($relative) && $this->fs->delete($relative);
$this->fs->writeStream($relative, $resource);

// Extracts the zipfile.
$zip = new ZipArchive;
$zip->open($absolute . $repo . '.zip');
$zip->extractTo($absolute);
$status = $zip->open($absolute . '.zip', ZipArchive::CHECKCONS);
if (true === $status) $status = $zip->extractTo(dirname($absolute));
$zip->close();

$this->fs->delete($author . '/' . $repo . '.zip');
$this->fs->deleteDir($author . '/' . $repo);
$this->fs->rename($author . '/' . $repo . '-' . $branch, $author . '/' . $repo);
}
// $status = false when extractTo() failed.
// Otherwise $status has one of the error code constants:
// https://www.php.net/manual/en/ziparchive.open.php
if ($status !== true) throw new Exception('ZipArchive failed', $status);

/**
* Get CURL response.
*
* @param String $url
* @return String $response
*/
private function get($url)
{
$this->curl->get($url);
// Removes the zipfile.
$this->fs->delete($relative);

// Strips the '.zip' from the path.
$relative = substr($relative, 0, -4);

if ($this->curl->error)
return 'Curl error: ' . $this->curl->error_code;
// Handles according to Flysystem version,
// 'rename' only exists in Flysystem 1.
if (method_exists($this->fs, 'rename')) {
$this->fs->deleteDir($relative);
$this->fs->rename($relative . '-' . $branch, $relative);
} else {
$this->fs->deleteDirectory($relative);
$this->fs->move($relative . '-' . $branch, $relative);
}

return $this->curl->response;
return $absolute;
}
}
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2017 wbadrh
Copyright (c) 2022 drhino

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand All @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
42 changes: 34 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,50 @@
# Download a Github repository with PHP.

This script will download the requested zip-archive with CURL.
Afterwards it will unpack with ZipArchive.

The Github repository will be downloaded in: GithubAuthor/Repository/
### Install with composer:

```sh
$ composer require wbadrh/git-dl
```

### Example usage:

```php
<?php

$git = new GitDownload(__DIR__ . '/downloaded');
$git = new GitDownload('/your/downloads');

$author = 'wbadrh';
$author = 'drhino';
$repository = 'git-dl';
$branch = 'master';

$git->clone($author, $repository, $branch);
$path = $git->clone($author, $repository, $branch);

// Prints: 'Saved to: /your/downloads/drhino/git-dl'
echo "Saved to: $path";

```

Would download in: downloaded/wbadrh/git-dl/
## How does it work?

The zip-archive is downloaded from Github. Then unpacked with ZipArchive.
<br>From version 1.1.x and above; A stream is used to keep a low memory footprint.
<br>After a succesful unpack, the downloaded archive is removed.
<br>Cloning an existing repository updates to the latest version.

## Changelog:

v1.1.0
- Uses a stream for writing.
- The PHP cURL extension is no longer used.
- Supports both Flysystem 1 and 3 (PHP 7 and 8).
- Returns the directory path on success.
- Throws Exception.

v1.0.2
- Fixes CVE-2021-32708.

v1.0.1
- Adds documentation.

v1.0.0
- Initial release.
6 changes: 2 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
"type": "library",
"description": "Clone a Github repository with PHP & ZipArchive. Without using Git or exec()!",
"keywords": ["git", "clone", "download", "repository", "github", "ziparchive"],
"homepage": "https://github.com/wbadrh/git-dl",
"homepage": "https://github.com/drhino/git-dl",
"license": "MIT",
"require": {
"ext-zip": "*",
"lib-curl": "*",
"curl/curl": "^1.7",
"league/flysystem": "^1.1.4"
"league/flysystem": "^1.1.4 || ^3.0"
},
"autoload": {
"psr-0": { "GitDownload": "" }
Expand Down
6 changes: 4 additions & 2 deletions test.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

$git = new GitDownload(__DIR__ . '/downloaded');

$author = 'wbadrh';
$author = 'drhino';
$repository = 'git-dl';
$branch = 'master';

$git->clone($author, $repository, $branch);
$path = $git->clone($author, $repository, $branch);

echo "Saved to: $path";

0 comments on commit 5b2fbde

Please sign in to comment.