Files
php-sdk-binary-tools/lib/php/libsdk/SDK/FileOps.php
Christoph M. Becker a047c663b9 Code clean-up (GH-22)
* 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.
2024-08-25 18:03:45 +02:00

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
*/