mirror of
https://github.com/php-win-ext/php-sdk-binary-tools.git
synced 2026-03-24 17:12:12 +01:00
* Declare methods and instance variables in abstract classes While PHP doesn't require this, it is good style, and makes the code easier to reason about. * Declare class variable While `Config::getSdkNugetFeedUrl()` is not used from within the project, and probably just was some experimental stuff, the method is public, so we better keep it for now, but avoid reading an undeclared class variable. * Dodge from covariance warnings regarding Iterator for now * Don't pass unnecessary arguments These are retrieved from within the called functions, what *might* not be the best idea, but it's what we have for now. * Assert that $cb is actually a string before string conversion In practice, it's either "copy" or "rename". * Declare missing `Server::getPhp()` * Remove unreachable code * Ensure that `$crt` is defined From looking at the code, it seems so, but let's make sure before reading an undefined variable. * Config::getDepsPort() returns an int While that is changing the signature of a public method, it makes no sense to return a string, and later to convert to int again. * The abstract class TrainingCase is an Interfaces\TrainingCase There shouldn't be the need for even having an interface (an abstract class should be sufficient), but we keep the interface for now.
232 lines
5.1 KiB
PHP
232 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace SDK;
|
|
|
|
trait FileOps
|
|
{
|
|
protected function md(string $name = "", bool $tmp = false) : string
|
|
{/*{{{*/
|
|
$ret = $name;
|
|
|
|
if (!$name) {
|
|
if ($tmp) {
|
|
$pre = Config::getTmpDir();
|
|
$ret = $pre . DIRECTORY_SEPARATOR . md5(uniqid());
|
|
} else {
|
|
throw new Exception("Dir name is empty");
|
|
}
|
|
}
|
|
|
|
|
|
if (!is_dir($ret)) {
|
|
if (!mkdir($ret, 0755, true)) {
|
|
throw new Exception("Unable to create '$ret'");
|
|
}
|
|
}
|
|
|
|
return $ret;
|
|
}/*}}}*/
|
|
|
|
/* TODO is link and more checks. */
|
|
protected function rm(string $path) : bool
|
|
{/*{{{*/
|
|
if (!file_exists($path)) {
|
|
return false;
|
|
} else if (is_file($path)) {
|
|
return unlink($path);
|
|
}
|
|
|
|
$ret = true;
|
|
|
|
$iterator = new \RecursiveIteratorIterator(
|
|
new \RecursiveDirectoryIterator(
|
|
$path,
|
|
\FilesystemIterator::SKIP_DOTS
|
|
),
|
|
\RecursiveIteratorIterator::CHILD_FIRST
|
|
);
|
|
foreach ($iterator as $item) {
|
|
if ($item->isDir()) {
|
|
$ret = $ret && rmdir($item->getPathname());
|
|
} else {
|
|
$ret = $ret && unlink($item->getPathname());
|
|
}
|
|
}
|
|
return $ret && rmdir($path);
|
|
}/*}}}*/
|
|
|
|
/* TODO islink and more checks */
|
|
protected function cp_or_mv(string $src, string $dst, callable $cb) : bool
|
|
{/*{{{*/
|
|
if (!file_exists($src)) {
|
|
return false;
|
|
} else if (is_file($src)) {
|
|
return call_user_func($cb, $src, $dst);
|
|
}
|
|
|
|
if (!file_exists($dst)) {
|
|
$this->md($dst);
|
|
}
|
|
|
|
$iterator = new \RecursiveIteratorIterator(
|
|
new \RecursiveDirectoryIterator(
|
|
$src,
|
|
\FilesystemIterator::SKIP_DOTS
|
|
),
|
|
\RecursiveIteratorIterator::CHILD_FIRST
|
|
);
|
|
$cut_len = strlen($src)+1;
|
|
foreach ($iterator as $item) {
|
|
$src_path = $item->getPathname();
|
|
$sub = substr($src_path, $cut_len);
|
|
$dst_path = $dst . DIRECTORY_SEPARATOR . $sub;
|
|
$dst_parent = dirname($dst_path);
|
|
|
|
if (!is_dir($dst_parent)) {
|
|
if (!$this->md($dst_parent)) {
|
|
throw new Exception("Unable to create '$dst_parent'");
|
|
}
|
|
}
|
|
|
|
if ($item->isFile()) {
|
|
if (!call_user_func($cb, $src_path, $dst_path)) {
|
|
assert(is_string($cb));
|
|
throw new Exception("Unable to $cb '$src_path' to '$dst_path'");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
}/*}}}*/
|
|
|
|
protected function cp(string $src, string $dst) : bool
|
|
{/*{{{*/
|
|
return $this->cp_or_mv($src, $dst, "copy");
|
|
}/*}}}*/
|
|
|
|
protected function mv(string $src, string $dst) : bool
|
|
{/*{{{*/
|
|
$ret = $this->cp_or_mv($src, $dst, "rename");
|
|
|
|
$ret = $ret && $this->rm($src);
|
|
|
|
return $ret;
|
|
}/*}}}*/
|
|
|
|
protected function download(string $url, string $dest_fn = NULL) : ?string
|
|
{/*{{{*/
|
|
$fd = NULL;
|
|
$retry = 0;
|
|
|
|
retry:
|
|
$ch = curl_init();
|
|
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
|
|
if ($dest_fn) {
|
|
$fd = fopen($dest_fn, "w+");
|
|
curl_setopt($ch, CURLOPT_FILE, $fd);
|
|
} else {
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
}
|
|
|
|
curl_setopt($ch, CURLOPT_HEADER, false);
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, Config::getSdkUserAgentName());
|
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
|
|
|
// workaround for <https://github.com/microsoft/php-sdk-binary-tools/issues/69>
|
|
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
|
|
|
$ret = curl_exec($ch);
|
|
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if (false === $ret || 200 !== $code) {
|
|
$err = curl_error($ch);
|
|
curl_close($ch);
|
|
if ($dest_fn) {
|
|
fclose($fd);
|
|
}
|
|
if ($retry++ < 3) {
|
|
goto retry;
|
|
}
|
|
throw new Exception($err);
|
|
}
|
|
|
|
curl_close($ch);
|
|
|
|
if ($dest_fn) {
|
|
fclose($fd);
|
|
return NULL;
|
|
}
|
|
|
|
return $ret;
|
|
}/*}}}*/
|
|
|
|
/* TODO More detailed zip errors. */
|
|
protected function unzip(string $zip_fn, string $dest_fn, string $dest_dn = NULL) : void
|
|
{/*{{{*/
|
|
$zip = new \ZipArchive;
|
|
|
|
$res = $zip->open($zip_fn);
|
|
if (true !== $res) {
|
|
throw new Exception("Failed to open '$zip_fn'.");
|
|
}
|
|
|
|
$res = $zip->extractTo($dest_fn);
|
|
if (true !== $res) {
|
|
$zip->close();
|
|
throw new Exception("Failed to unzip '$zip_fn'.");
|
|
}
|
|
|
|
/* Not robust, useful for zips containing one dir sibling only in the root. */
|
|
if ($dest_dn) {
|
|
$stat = $zip->statIndex(0);
|
|
if (false === $stat) {
|
|
$zip->close();
|
|
throw new Exception("Failed to stat first index in '$zip_fn'.");
|
|
}
|
|
|
|
$zip->close();
|
|
|
|
/* Index of zero might be not the zipped folder, unusual but true. */
|
|
/*$name = $stat["name"];
|
|
if ("/" != substr($name, -1)) {
|
|
throw new Exception("'$name' is not a directory.");
|
|
}
|
|
$name = substr($name, 0, -1);*/
|
|
|
|
$name = rtrim($stat["name"], "/");
|
|
while (strstr($name, '/') !== false) {
|
|
$name = dirname($name);
|
|
}
|
|
|
|
$old_dir = $dest_fn . DIRECTORY_SEPARATOR . $name;
|
|
$new_dir = $dest_fn . DIRECTORY_SEPARATOR . $dest_dn;
|
|
if (file_exists($new_dir)) {
|
|
if (!$this->rm($new_dir)) {
|
|
throw new Exception("Failed to remove '$new_dir'.");
|
|
}
|
|
}
|
|
/* if (!$this->mv($old_dir, $new_dir)) { */
|
|
if (!rename($old_dir, $new_dir)) {
|
|
throw new Exception("Failed to rename '$old_dir' to '$new_dir'.");
|
|
}
|
|
} else {
|
|
$zip->close();
|
|
}
|
|
}/*}}}*/
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
* vim600: sw=4 ts=4 fdm=marker
|
|
* vim<600: sw=4 ts=4
|
|
*/
|