PHP 8.4 compatibility
This commit is contained in:
@@ -125,6 +125,7 @@
|
||||
"Faker\\Provider\\": "overrides/fzaninotto/faker/src/Faker/Provider/",
|
||||
"Illuminate\\Database\\": "overrides/laravel/framework/src/Illuminate/Database/",
|
||||
|
||||
"Chumper\\Zipper\\": "overrides/chumper/zipper/src/Chumper/Zipper/",
|
||||
"Illuminate\\Support\\": "overrides/laravel/framework/src/Illuminate/Support/",
|
||||
"Illuminate\\Http\\": "overrides/laravel/framework/src/Illuminate/Http/",
|
||||
"Illuminate\\Database\\Eloquent\\": "overrides/laravel/framework/src/Illuminate/Database/Eloquent/",
|
||||
@@ -349,6 +350,9 @@
|
||||
"vendor/guzzlehttp/psr7/src/Uri.php",
|
||||
"vendor/guzzlehttp/promises/src/RejectedPromise.php",
|
||||
"vendor/guzzlehttp/promises/src/PromiseInterface.php",
|
||||
"vendor/guzzlehttp/promises/src/FulfilledPromise.php",
|
||||
"vendor/guzzlehttp/promises/src/Utils.php",
|
||||
"vendor/guzzlehttp/promises/src/Promise.php",
|
||||
"vendor/ramsey/uuid/src/Uuid.php",
|
||||
"vendor/ramsey/uuid/src/UuidFactory.php",
|
||||
"vendor/laravel/framework/src/Illuminate/Routing/Router.php",
|
||||
@@ -441,7 +445,8 @@
|
||||
"vendor/filp/whoops/src/Whoops/Util/TemplateHelper.php",
|
||||
"vendor/laravel/framework/src/Illuminate/Validation/Factory.php",
|
||||
"vendor/laravel/framework/src/Illuminate/Validation/ValidationRuleParser.php",
|
||||
"vendor/laravel/framework/src/Illuminate/Foundation/Validation/ValidatesRequests.php"
|
||||
"vendor/laravel/framework/src/Illuminate/Foundation/Validation/ValidatesRequests.php",
|
||||
"vendor/chumper/zipper/src/Chumper/Zipper/Zipper.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
|
||||
622
overrides/chumper/zipper/src/Chumper/Zipper/Zipper.php
Normal file
622
overrides/chumper/zipper/src/Chumper/Zipper/Zipper.php
Normal file
@@ -0,0 +1,622 @@
|
||||
<?php
|
||||
|
||||
namespace Chumper\Zipper;
|
||||
|
||||
use Chumper\Zipper\Repositories\RepositoryInterface;
|
||||
use Exception;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
|
||||
/**
|
||||
* This Zipper class is a wrapper around the ZipArchive methods with some handy functions
|
||||
*
|
||||
* Class Zipper
|
||||
*/
|
||||
class Zipper
|
||||
{
|
||||
/**
|
||||
* Constant for extracting
|
||||
*/
|
||||
const WHITELIST = 1;
|
||||
|
||||
/**
|
||||
* Constant for extracting
|
||||
*/
|
||||
const BLACKLIST = 2;
|
||||
|
||||
/**
|
||||
* Constant for matching only strictly equal file names
|
||||
*/
|
||||
const EXACT_MATCH = 4;
|
||||
|
||||
/**
|
||||
* @var string Represents the current location in the archive
|
||||
*/
|
||||
private $currentFolder = '';
|
||||
|
||||
/**
|
||||
* @var Filesystem Handler to the file system
|
||||
*/
|
||||
private $file;
|
||||
|
||||
/**
|
||||
* @var RepositoryInterface Handler to the archive
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var string The path to the current zip file
|
||||
*/
|
||||
private $filePath;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Filesystem $fs
|
||||
*/
|
||||
public function __construct(?Filesystem $fs = null)
|
||||
{
|
||||
$this->file = $fs ? $fs : new Filesystem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (null !== $this->repository) {
|
||||
$this->repository->close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new zip Archive if the file does not exists
|
||||
* opens a zip archive if the file exists
|
||||
*
|
||||
* @param $pathToFile string The file to open
|
||||
* @param RepositoryInterface|string $type The type of the archive, defaults to zip, possible are zip, phar
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws \Exception
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return $this Zipper instance
|
||||
*/
|
||||
public function make($pathToFile, $type = 'zip')
|
||||
{
|
||||
$new = $this->createArchiveFile($pathToFile);
|
||||
$this->filePath = $pathToFile;
|
||||
|
||||
$objectOrName = $type;
|
||||
if (is_string($type)) {
|
||||
$objectOrName = 'Chumper\Zipper\Repositories\\'.ucwords($type).'Repository';
|
||||
}
|
||||
|
||||
if (!is_subclass_of($objectOrName, 'Chumper\Zipper\Repositories\RepositoryInterface')) {
|
||||
throw new \InvalidArgumentException("Class for '{$objectOrName}' must implement RepositoryInterface interface");
|
||||
}
|
||||
|
||||
$this->repository = $type;
|
||||
if (is_string($objectOrName)) {
|
||||
$this->repository = new $objectOrName($pathToFile, $new);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new zip archive or open an existing one
|
||||
*
|
||||
* @param $pathToFile
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function zip($pathToFile)
|
||||
{
|
||||
$this->make($pathToFile);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new phar file or open one
|
||||
*
|
||||
* @param $pathToFile
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function phar($pathToFile)
|
||||
{
|
||||
$this->make($pathToFile, 'phar');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new rar file or open one
|
||||
*
|
||||
* @param $pathToFile
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function rar($pathToFile)
|
||||
{
|
||||
$this->make($pathToFile, 'rar');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the opened zip archive to the specified location <br/>
|
||||
* you can provide an array of files and folders and define if they should be a white list
|
||||
* or a black list to extract. By default this method compares file names using "string starts with" logic
|
||||
*
|
||||
* @param $path string The path to extract to
|
||||
* @param array $files An array of files
|
||||
* @param int $methodFlags The Method the files should be treated
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function extractTo($path, array $files = [], $methodFlags = self::BLACKLIST)
|
||||
{
|
||||
if (!$this->file->exists($path) && !$this->file->makeDirectory($path, 0755, true)) {
|
||||
throw new \RuntimeException('Failed to create folder');
|
||||
}
|
||||
|
||||
if ($methodFlags & self::EXACT_MATCH) {
|
||||
$matchingMethod = function ($haystack) use ($files) {
|
||||
return in_array($haystack, $files, true);
|
||||
};
|
||||
} else {
|
||||
$matchingMethod = function ($haystack) use ($files) {
|
||||
return starts_with($haystack, $files);
|
||||
};
|
||||
}
|
||||
|
||||
if ($methodFlags & self::WHITELIST) {
|
||||
$this->extractFilesInternal($path, $matchingMethod);
|
||||
} else {
|
||||
// blacklist - extract files that do not match with $matchingMethod
|
||||
$this->extractFilesInternal($path, function ($filename) use ($matchingMethod) {
|
||||
return !$matchingMethod($filename);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts matching files/folders from the opened zip archive to the specified location.
|
||||
*
|
||||
* @param string $extractToPath The path to extract to
|
||||
* @param string $regex regular expression used to match files. See @link http://php.net/manual/en/reference.pcre.pattern.syntax.php
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function extractMatchingRegex($extractToPath, $regex)
|
||||
{
|
||||
if (empty($regex)) {
|
||||
throw new \InvalidArgumentException('Missing pass valid regex parameter');
|
||||
}
|
||||
|
||||
$this->extractFilesInternal($extractToPath, function ($filename) use ($regex) {
|
||||
$match = preg_match($regex, $filename);
|
||||
if ($match === 1) {
|
||||
return true;
|
||||
} elseif ($match === false) {
|
||||
//invalid pattern for preg_match raises E_WARNING and returns FALSE
|
||||
//so if you have custom error_handler set to catch and throw E_WARNINGs you never end up here
|
||||
//but if you have not - this will throw exception
|
||||
throw new \RuntimeException("regular expression match on '$filename' failed with error. Please check if pattern is valid regular expression.");
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of a single file if available
|
||||
*
|
||||
* @param $filePath string The full path (including all folders) of the file in the zip
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return mixed returns the content or throws an exception
|
||||
*/
|
||||
public function getFileContent($filePath)
|
||||
{
|
||||
if ($this->repository->fileExists($filePath) === false) {
|
||||
throw new Exception(sprintf('The file "%s" cannot be found', $filePath));
|
||||
}
|
||||
return $this->repository->getFileContent($filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or multiple files to the zip.
|
||||
*
|
||||
* @param $pathToAdd array|string An array or string of files and folders to add
|
||||
* @param null|mixed $fileName
|
||||
*
|
||||
* @return $this Zipper instance
|
||||
*/
|
||||
public function add($pathToAdd, $fileName = null)
|
||||
{
|
||||
if (is_array($pathToAdd)) {
|
||||
foreach ($pathToAdd as $key=>$dir) {
|
||||
if (!is_int($key)) {
|
||||
$this->add($dir, $key); }
|
||||
else {
|
||||
$this->add($dir);
|
||||
}
|
||||
}
|
||||
} elseif ($this->file->isFile($pathToAdd)) {
|
||||
if ($fileName) {
|
||||
$this->addFile($pathToAdd, $fileName);
|
||||
} else {
|
||||
$this->addFile($pathToAdd);
|
||||
}
|
||||
} else {
|
||||
$this->addDir($pathToAdd);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an empty directory
|
||||
*
|
||||
* @param $dirName
|
||||
*
|
||||
* @return Zipper
|
||||
*/
|
||||
public function addEmptyDir($dirName)
|
||||
{
|
||||
$this->repository->addEmptyDir($dirName);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a file to the zip using its contents
|
||||
*
|
||||
* @param $filename string The name of the file to create
|
||||
* @param $content string The file contents
|
||||
*
|
||||
* @return $this Zipper instance
|
||||
*/
|
||||
public function addString($filename, $content)
|
||||
{
|
||||
$this->addFromString($filename, $content);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status of the zip.
|
||||
*
|
||||
* @return int The status of the internal zip file
|
||||
*/
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->repository->getStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a file or array of files and folders from the zip archive
|
||||
*
|
||||
* @param $fileToRemove array|string The path/array to the files in the zip
|
||||
*
|
||||
* @return $this Zipper instance
|
||||
*/
|
||||
public function remove($fileToRemove)
|
||||
{
|
||||
if (is_array($fileToRemove)) {
|
||||
$self = $this;
|
||||
$this->repository->each(function ($file) use ($fileToRemove, $self) {
|
||||
if (starts_with($file, $fileToRemove)) {
|
||||
$self->getRepository()->removeFile($file);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$this->repository->removeFile($fileToRemove);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path of the current zip file if there is one.
|
||||
*
|
||||
* @return string The path to the file
|
||||
*/
|
||||
public function getFilePath()
|
||||
{
|
||||
return $this->filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the password to be used for decompressing
|
||||
*
|
||||
* @param $password
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function usePassword($password)
|
||||
{
|
||||
return $this->repository->usePassword($password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the zip file and frees all handles
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (null !== $this->repository) {
|
||||
$this->repository->close();
|
||||
}
|
||||
$this->filePath = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal folder to the given path.<br/>
|
||||
* Useful for extracting only a segment of a zip file.
|
||||
*
|
||||
* @param $path
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function folder($path)
|
||||
{
|
||||
$this->currentFolder = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the internal folder to the root of the zip file.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function home()
|
||||
{
|
||||
$this->currentFolder = '';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the archive file
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if (null !== $this->repository) {
|
||||
$this->repository->close();
|
||||
}
|
||||
|
||||
$this->file->delete($this->filePath);
|
||||
$this->filePath = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the Archive
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getArchiveType()
|
||||
{
|
||||
return get_class($this->repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current internal folder pointer
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrentFolderPath()
|
||||
{
|
||||
return $this->currentFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a file is present in the archive
|
||||
*
|
||||
* @param $fileInArchive
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function contains($fileInArchive)
|
||||
{
|
||||
return $this->repository->fileExists($fileInArchive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
public function getRepository()
|
||||
{
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Filesystem
|
||||
*/
|
||||
public function getFileHandler()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to the internal folder
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInternalPath()
|
||||
{
|
||||
return empty($this->currentFolder) ? '' : $this->currentFolder.'/';
|
||||
}
|
||||
|
||||
/**
|
||||
* List all files that are within the archive
|
||||
*
|
||||
* @param string|null $regexFilter regular expression to filter returned files/folders. See @link http://php.net/manual/en/reference.pcre.pattern.syntax.php
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listFiles($regexFilter = null)
|
||||
{
|
||||
$filesList = [];
|
||||
if ($regexFilter) {
|
||||
$filter = function ($file) use (&$filesList, $regexFilter) {
|
||||
// push/pop an error handler here to to make sure no error/exception thrown if $expected is not a regex
|
||||
set_error_handler(function () {
|
||||
});
|
||||
$match = preg_match($regexFilter, $file);
|
||||
restore_error_handler();
|
||||
|
||||
if ($match === 1) {
|
||||
$filesList[] = $file;
|
||||
} elseif ($match === false) {
|
||||
throw new \RuntimeException("regular expression match on '$file' failed with error. Please check if pattern is valid regular expression.");
|
||||
}
|
||||
};
|
||||
} else {
|
||||
$filter = function ($file) use (&$filesList) {
|
||||
$filesList[] = $file;
|
||||
};
|
||||
}
|
||||
$this->repository->each($filter);
|
||||
|
||||
return $filesList;
|
||||
}
|
||||
|
||||
private function getCurrentFolderWithTrailingSlash()
|
||||
{
|
||||
if (empty($this->currentFolder)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$lastChar = mb_substr($this->currentFolder, -1);
|
||||
if ($lastChar !== '/' || $lastChar !== '\\') {
|
||||
return $this->currentFolder.'/';
|
||||
}
|
||||
|
||||
return $this->currentFolder;
|
||||
}
|
||||
|
||||
//---------------------PRIVATE FUNCTIONS-------------
|
||||
|
||||
/**
|
||||
* @param $pathToZip
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function createArchiveFile($pathToZip)
|
||||
{
|
||||
if (!$this->file->exists($pathToZip)) {
|
||||
$dirname = dirname($pathToZip);
|
||||
if (!$this->file->exists($dirname) && !$this->file->makeDirectory($dirname, 0755, true)) {
|
||||
throw new \RuntimeException('Failed to create folder');
|
||||
} elseif (!$this->file->isWritable($dirname)) {
|
||||
throw new Exception(sprintf('The path "%s" is not writeable', $pathToZip));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $pathToDir
|
||||
*/
|
||||
private function addDir($pathToDir)
|
||||
{
|
||||
// First go over the files in this directory and add them to the repository.
|
||||
foreach ($this->file->files($pathToDir) as $file) {
|
||||
$this->addFile($pathToDir.'/'.basename($file));
|
||||
}
|
||||
|
||||
// Now let's visit the subdirectories and add them, too.
|
||||
foreach ($this->file->directories($pathToDir) as $dir) {
|
||||
$old_folder = $this->currentFolder;
|
||||
$this->currentFolder = empty($this->currentFolder) ? basename($dir) : $this->currentFolder.'/'.basename($dir);
|
||||
$this->addDir($pathToDir.'/'.basename($dir));
|
||||
$this->currentFolder = $old_folder;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the file to the zip
|
||||
*
|
||||
* @param string $pathToAdd
|
||||
* @param string $fileName
|
||||
*/
|
||||
private function addFile($pathToAdd, $fileName = null)
|
||||
{
|
||||
$info = pathinfo($pathToAdd);
|
||||
|
||||
if (!$fileName) {
|
||||
$fileName = isset($info['extension']) ?
|
||||
$info['filename'].'.'.$info['extension'] :
|
||||
$info['filename'];
|
||||
}
|
||||
|
||||
$this->repository->addFile($pathToAdd, $this->getInternalPath().$fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the file to the zip from content
|
||||
*
|
||||
* @param $filename
|
||||
* @param $content
|
||||
*/
|
||||
private function addFromString($filename, $content)
|
||||
{
|
||||
$this->repository->addFromString($this->getInternalPath().$filename, $content);
|
||||
}
|
||||
|
||||
private function extractFilesInternal($path, callable $matchingMethod)
|
||||
{
|
||||
$self = $this;
|
||||
$this->repository->each(function ($fileName) use ($path, $matchingMethod, $self) {
|
||||
$currentPath = $self->getCurrentFolderWithTrailingSlash();
|
||||
if (!empty($currentPath) && !starts_with($fileName, $currentPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filename = str_replace($self->getInternalPath(), '', $fileName);
|
||||
if ($matchingMethod($filename)) {
|
||||
$self->extractOneFileInternal($fileName, $path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $fileName
|
||||
* @param $path
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
private function extractOneFileInternal($fileName, $path)
|
||||
{
|
||||
$tmpPath = str_replace($this->getInternalPath(), '', $fileName);
|
||||
|
||||
// We need to create the directory first in case it doesn't exist
|
||||
$dir = pathinfo($path.DIRECTORY_SEPARATOR.$tmpPath, PATHINFO_DIRNAME);
|
||||
if (!$this->file->exists($dir) && !$this->file->makeDirectory($dir, 0755, true, true)) {
|
||||
throw new \RuntimeException('Failed to create folders');
|
||||
}
|
||||
|
||||
$toPath = $path.DIRECTORY_SEPARATOR.$tmpPath;
|
||||
$fileStream = $this->getRepository()->getFileStream($fileName);
|
||||
$this->getFileHandler()->put($toPath, $fileStream);
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ abstract class BaseProvider implements Countable
|
||||
/**
|
||||
* @param null $publicPath
|
||||
*/
|
||||
public function __construct($publicPath = null, $config = null, Filesystem $file = null)
|
||||
public function __construct($publicPath = null, $config = null, ?Filesystem $file = null)
|
||||
{
|
||||
$this->file = $file ?: new Filesystem;
|
||||
|
||||
|
||||
84
overrides/guzzlehttp/promises/src/FulfilledPromise.php
Normal file
84
overrides/guzzlehttp/promises/src/FulfilledPromise.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
* A promise that has been fulfilled.
|
||||
*
|
||||
* Thenning off of this promise will invoke the onFulfilled callback
|
||||
* immediately and ignore other callbacks.
|
||||
*/
|
||||
class FulfilledPromise implements PromiseInterface
|
||||
{
|
||||
private $value;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
if (is_object($value) && method_exists($value, 'then')) {
|
||||
throw new \InvalidArgumentException(
|
||||
'You cannot create a FulfilledPromise with a promise.'
|
||||
);
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function then(
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null
|
||||
) {
|
||||
// Return itself if there is no onFulfilled function.
|
||||
if (!$onFulfilled) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$queue = Utils::queue();
|
||||
$p = new Promise([$queue, 'run']);
|
||||
$value = $this->value;
|
||||
$queue->add(static function () use ($p, $value, $onFulfilled) {
|
||||
if (Is::pending($p)) {
|
||||
try {
|
||||
$p->resolve($onFulfilled($value));
|
||||
} catch (\Throwable $e) {
|
||||
$p->reject($e);
|
||||
} catch (\Exception $e) {
|
||||
$p->reject($e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return $p;
|
||||
}
|
||||
|
||||
public function otherwise(callable $onRejected)
|
||||
{
|
||||
return $this->then(null, $onRejected);
|
||||
}
|
||||
|
||||
public function wait($unwrap = true, $defaultDelivery = null)
|
||||
{
|
||||
return $unwrap ? $this->value : null;
|
||||
}
|
||||
|
||||
public function getState()
|
||||
{
|
||||
return self::FULFILLED;
|
||||
}
|
||||
|
||||
public function resolve($value)
|
||||
{
|
||||
if ($value !== $this->value) {
|
||||
throw new \LogicException("Cannot resolve a fulfilled promise");
|
||||
}
|
||||
}
|
||||
|
||||
public function reject($reason)
|
||||
{
|
||||
throw new \LogicException("Cannot reject a fulfilled promise");
|
||||
}
|
||||
|
||||
public function cancel()
|
||||
{
|
||||
// pass
|
||||
}
|
||||
}
|
||||
278
overrides/guzzlehttp/promises/src/Promise.php
Normal file
278
overrides/guzzlehttp/promises/src/Promise.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
/**
|
||||
* Promises/A+ implementation that avoids recursion when possible.
|
||||
*
|
||||
* @link https://promisesaplus.com/
|
||||
*/
|
||||
class Promise implements PromiseInterface
|
||||
{
|
||||
private $state = self::PENDING;
|
||||
private $result;
|
||||
private $cancelFn;
|
||||
private $waitFn;
|
||||
private $waitList;
|
||||
private $handlers = [];
|
||||
|
||||
/**
|
||||
* @param callable $waitFn Fn that when invoked resolves the promise.
|
||||
* @param callable $cancelFn Fn that when invoked cancels the promise.
|
||||
*/
|
||||
public function __construct(
|
||||
?callable $waitFn = null,
|
||||
?callable $cancelFn = null
|
||||
) {
|
||||
$this->waitFn = $waitFn;
|
||||
$this->cancelFn = $cancelFn;
|
||||
}
|
||||
|
||||
public function then(
|
||||
?callable $onFulfilled = null,
|
||||
?callable $onRejected = null
|
||||
) {
|
||||
if ($this->state === self::PENDING) {
|
||||
$p = new Promise(null, [$this, 'cancel']);
|
||||
$this->handlers[] = [$p, $onFulfilled, $onRejected];
|
||||
$p->waitList = $this->waitList;
|
||||
$p->waitList[] = $this;
|
||||
return $p;
|
||||
}
|
||||
|
||||
// Return a fulfilled promise and immediately invoke any callbacks.
|
||||
if ($this->state === self::FULFILLED) {
|
||||
$promise = Create::promiseFor($this->result);
|
||||
return $onFulfilled ? $promise->then($onFulfilled) : $promise;
|
||||
}
|
||||
|
||||
// It's either cancelled or rejected, so return a rejected promise
|
||||
// and immediately invoke any callbacks.
|
||||
$rejection = Create::rejectionFor($this->result);
|
||||
return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
|
||||
}
|
||||
|
||||
public function otherwise(callable $onRejected)
|
||||
{
|
||||
return $this->then(null, $onRejected);
|
||||
}
|
||||
|
||||
public function wait($unwrap = true)
|
||||
{
|
||||
$this->waitIfPending();
|
||||
|
||||
if ($this->result instanceof PromiseInterface) {
|
||||
return $this->result->wait($unwrap);
|
||||
}
|
||||
if ($unwrap) {
|
||||
if ($this->state === self::FULFILLED) {
|
||||
return $this->result;
|
||||
}
|
||||
// It's rejected so "unwrap" and throw an exception.
|
||||
throw Create::exceptionFor($this->result);
|
||||
}
|
||||
}
|
||||
|
||||
public function getState()
|
||||
{
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
public function cancel()
|
||||
{
|
||||
if ($this->state !== self::PENDING) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->waitFn = $this->waitList = null;
|
||||
|
||||
if ($this->cancelFn) {
|
||||
$fn = $this->cancelFn;
|
||||
$this->cancelFn = null;
|
||||
try {
|
||||
$fn();
|
||||
} catch (\Throwable $e) {
|
||||
$this->reject($e);
|
||||
} catch (\Exception $e) {
|
||||
$this->reject($e);
|
||||
}
|
||||
}
|
||||
|
||||
// Reject the promise only if it wasn't rejected in a then callback.
|
||||
/** @psalm-suppress RedundantCondition */
|
||||
if ($this->state === self::PENDING) {
|
||||
$this->reject(new CancellationException('Promise has been cancelled'));
|
||||
}
|
||||
}
|
||||
|
||||
public function resolve($value)
|
||||
{
|
||||
$this->settle(self::FULFILLED, $value);
|
||||
}
|
||||
|
||||
public function reject($reason)
|
||||
{
|
||||
$this->settle(self::REJECTED, $reason);
|
||||
}
|
||||
|
||||
private function settle($state, $value)
|
||||
{
|
||||
if ($this->state !== self::PENDING) {
|
||||
// Ignore calls with the same resolution.
|
||||
if ($state === $this->state && $value === $this->result) {
|
||||
return;
|
||||
}
|
||||
throw $this->state === $state
|
||||
? new \LogicException("The promise is already {$state}.")
|
||||
: new \LogicException("Cannot change a {$this->state} promise to {$state}");
|
||||
}
|
||||
|
||||
if ($value === $this) {
|
||||
throw new \LogicException('Cannot fulfill or reject a promise with itself');
|
||||
}
|
||||
|
||||
// Clear out the state of the promise but stash the handlers.
|
||||
$this->state = $state;
|
||||
$this->result = $value;
|
||||
$handlers = $this->handlers;
|
||||
$this->handlers = null;
|
||||
$this->waitList = $this->waitFn = null;
|
||||
$this->cancelFn = null;
|
||||
|
||||
if (!$handlers) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the value was not a settled promise or a thenable, then resolve
|
||||
// it in the task queue using the correct ID.
|
||||
if (!is_object($value) || !method_exists($value, 'then')) {
|
||||
$id = $state === self::FULFILLED ? 1 : 2;
|
||||
// It's a success, so resolve the handlers in the queue.
|
||||
Utils::queue()->add(static function () use ($id, $value, $handlers) {
|
||||
foreach ($handlers as $handler) {
|
||||
self::callHandler($id, $value, $handler);
|
||||
}
|
||||
});
|
||||
} elseif ($value instanceof Promise && Is::pending($value)) {
|
||||
// We can just merge our handlers onto the next promise.
|
||||
$value->handlers = array_merge($value->handlers, $handlers);
|
||||
} else {
|
||||
// Resolve the handlers when the forwarded promise is resolved.
|
||||
$value->then(
|
||||
static function ($value) use ($handlers) {
|
||||
foreach ($handlers as $handler) {
|
||||
self::callHandler(1, $value, $handler);
|
||||
}
|
||||
},
|
||||
static function ($reason) use ($handlers) {
|
||||
foreach ($handlers as $handler) {
|
||||
self::callHandler(2, $reason, $handler);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a stack of handlers using a specific callback index and value.
|
||||
*
|
||||
* @param int $index 1 (resolve) or 2 (reject).
|
||||
* @param mixed $value Value to pass to the callback.
|
||||
* @param array $handler Array of handler data (promise and callbacks).
|
||||
*/
|
||||
private static function callHandler($index, $value, array $handler)
|
||||
{
|
||||
/** @var PromiseInterface $promise */
|
||||
$promise = $handler[0];
|
||||
|
||||
// The promise may have been cancelled or resolved before placing
|
||||
// this thunk in the queue.
|
||||
if (Is::settled($promise)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (isset($handler[$index])) {
|
||||
/*
|
||||
* If $f throws an exception, then $handler will be in the exception
|
||||
* stack trace. Since $handler contains a reference to the callable
|
||||
* itself we get a circular reference. We clear the $handler
|
||||
* here to avoid that memory leak.
|
||||
*/
|
||||
$f = $handler[$index];
|
||||
unset($handler);
|
||||
$promise->resolve($f($value));
|
||||
} elseif ($index === 1) {
|
||||
// Forward resolution values as-is.
|
||||
$promise->resolve($value);
|
||||
} else {
|
||||
// Forward rejections down the chain.
|
||||
$promise->reject($value);
|
||||
}
|
||||
} catch (\Throwable $reason) {
|
||||
$promise->reject($reason);
|
||||
} catch (\Exception $reason) {
|
||||
$promise->reject($reason);
|
||||
}
|
||||
}
|
||||
|
||||
private function waitIfPending()
|
||||
{
|
||||
if ($this->state !== self::PENDING) {
|
||||
return;
|
||||
} elseif ($this->waitFn) {
|
||||
$this->invokeWaitFn();
|
||||
} elseif ($this->waitList) {
|
||||
$this->invokeWaitList();
|
||||
} else {
|
||||
// If there's no wait function, then reject the promise.
|
||||
$this->reject('Cannot wait on a promise that has '
|
||||
. 'no internal wait function. You must provide a wait '
|
||||
. 'function when constructing the promise to be able to '
|
||||
. 'wait on a promise.');
|
||||
}
|
||||
|
||||
Utils::queue()->run();
|
||||
|
||||
/** @psalm-suppress RedundantCondition */
|
||||
if ($this->state === self::PENDING) {
|
||||
$this->reject('Invoking the wait callback did not resolve the promise');
|
||||
}
|
||||
}
|
||||
|
||||
private function invokeWaitFn()
|
||||
{
|
||||
try {
|
||||
$wfn = $this->waitFn;
|
||||
$this->waitFn = null;
|
||||
$wfn(true);
|
||||
} catch (\Exception $reason) {
|
||||
if ($this->state === self::PENDING) {
|
||||
// The promise has not been resolved yet, so reject the promise
|
||||
// with the exception.
|
||||
$this->reject($reason);
|
||||
} else {
|
||||
// The promise was already resolved, so there's a problem in
|
||||
// the application.
|
||||
throw $reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function invokeWaitList()
|
||||
{
|
||||
$waitList = $this->waitList;
|
||||
$this->waitList = null;
|
||||
|
||||
foreach ($waitList as $result) {
|
||||
do {
|
||||
$result->waitIfPending();
|
||||
$result = $result->result;
|
||||
} while ($result instanceof Promise);
|
||||
|
||||
if ($result instanceof PromiseInterface) {
|
||||
$result->wait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
276
overrides/guzzlehttp/promises/src/Utils.php
Normal file
276
overrides/guzzlehttp/promises/src/Utils.php
Normal file
@@ -0,0 +1,276 @@
|
||||
<?php
|
||||
|
||||
namespace GuzzleHttp\Promise;
|
||||
|
||||
final class Utils
|
||||
{
|
||||
/**
|
||||
* Get the global task queue used for promise resolution.
|
||||
*
|
||||
* This task queue MUST be run in an event loop in order for promises to be
|
||||
* settled asynchronously. It will be automatically run when synchronously
|
||||
* waiting on a promise.
|
||||
*
|
||||
* <code>
|
||||
* while ($eventLoop->isRunning()) {
|
||||
* GuzzleHttp\Promise\Utils::queue()->run();
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* @param TaskQueueInterface $assign Optionally specify a new queue instance.
|
||||
*
|
||||
* @return TaskQueueInterface
|
||||
*/
|
||||
public static function queue(?TaskQueueInterface $assign = null)
|
||||
{
|
||||
static $queue;
|
||||
|
||||
if ($assign) {
|
||||
$queue = $assign;
|
||||
} elseif (!$queue) {
|
||||
$queue = new TaskQueue();
|
||||
}
|
||||
|
||||
return $queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a function to run in the task queue when it is next `run()` and
|
||||
* returns a promise that is fulfilled or rejected with the result.
|
||||
*
|
||||
* @param callable $task Task function to run.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public static function task(callable $task)
|
||||
{
|
||||
$queue = self::queue();
|
||||
$promise = new Promise([$queue, 'run']);
|
||||
$queue->add(function () use ($task, $promise) {
|
||||
try {
|
||||
if (Is::pending($promise)) {
|
||||
$promise->resolve($task());
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$promise->reject($e);
|
||||
} catch (\Exception $e) {
|
||||
$promise->reject($e);
|
||||
}
|
||||
});
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously waits on a promise to resolve and returns an inspection
|
||||
* state array.
|
||||
*
|
||||
* Returns a state associative array containing a "state" key mapping to a
|
||||
* valid promise state. If the state of the promise is "fulfilled", the
|
||||
* array will contain a "value" key mapping to the fulfilled value of the
|
||||
* promise. If the promise is rejected, the array will contain a "reason"
|
||||
* key mapping to the rejection reason of the promise.
|
||||
*
|
||||
* @param PromiseInterface $promise Promise or value.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function inspect(PromiseInterface $promise)
|
||||
{
|
||||
try {
|
||||
return [
|
||||
'state' => PromiseInterface::FULFILLED,
|
||||
'value' => $promise->wait()
|
||||
];
|
||||
} catch (RejectionException $e) {
|
||||
return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
|
||||
} catch (\Throwable $e) {
|
||||
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
|
||||
} catch (\Exception $e) {
|
||||
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits on all of the provided promises, but does not unwrap rejected
|
||||
* promises as thrown exception.
|
||||
*
|
||||
* Returns an array of inspection state arrays.
|
||||
*
|
||||
* @see inspect for the inspection state array format.
|
||||
*
|
||||
* @param PromiseInterface[] $promises Traversable of promises to wait upon.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function inspectAll($promises)
|
||||
{
|
||||
$results = [];
|
||||
foreach ($promises as $key => $promise) {
|
||||
$results[$key] = self::inspect($promise);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits on all of the provided promises and returns the fulfilled values.
|
||||
*
|
||||
* Returns an array that contains the value of each promise (in the same
|
||||
* order the promises were provided). An exception is thrown if any of the
|
||||
* promises are rejected.
|
||||
*
|
||||
* @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Exception on error
|
||||
* @throws \Throwable on error in PHP >=7
|
||||
*/
|
||||
public static function unwrap($promises)
|
||||
{
|
||||
$results = [];
|
||||
foreach ($promises as $key => $promise) {
|
||||
$results[$key] = $promise->wait();
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of promises, return a promise that is fulfilled when all
|
||||
* the items in the array are fulfilled.
|
||||
*
|
||||
* The promise's fulfillment value is an array with fulfillment values at
|
||||
* respective positions to the original array. If any promise in the array
|
||||
* rejects, the returned promise is rejected with the rejection reason.
|
||||
*
|
||||
* @param mixed $promises Promises or values.
|
||||
* @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public static function all($promises, $recursive = false)
|
||||
{
|
||||
$results = [];
|
||||
$promise = Each::of(
|
||||
$promises,
|
||||
function ($value, $idx) use (&$results) {
|
||||
$results[$idx] = $value;
|
||||
},
|
||||
function ($reason, $idx, Promise $aggregate) {
|
||||
$aggregate->reject($reason);
|
||||
}
|
||||
)->then(function () use (&$results) {
|
||||
ksort($results);
|
||||
return $results;
|
||||
});
|
||||
|
||||
if (true === $recursive) {
|
||||
$promise = $promise->then(function ($results) use ($recursive, &$promises) {
|
||||
foreach ($promises as $promise) {
|
||||
if (Is::pending($promise)) {
|
||||
return self::all($promises, $recursive);
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
});
|
||||
}
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a competitive race between multiple promises or values (values
|
||||
* will become immediately fulfilled promises).
|
||||
*
|
||||
* When count amount of promises have been fulfilled, the returned promise
|
||||
* is fulfilled with an array that contains the fulfillment values of the
|
||||
* winners in order of resolution.
|
||||
*
|
||||
* This promise is rejected with a {@see AggregateException} if the number
|
||||
* of fulfilled promises is less than the desired $count.
|
||||
*
|
||||
* @param int $count Total number of promises.
|
||||
* @param mixed $promises Promises or values.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public static function some($count, $promises)
|
||||
{
|
||||
$results = [];
|
||||
$rejections = [];
|
||||
|
||||
return Each::of(
|
||||
$promises,
|
||||
function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
|
||||
if (Is::settled($p)) {
|
||||
return;
|
||||
}
|
||||
$results[$idx] = $value;
|
||||
if (count($results) >= $count) {
|
||||
$p->resolve(null);
|
||||
}
|
||||
},
|
||||
function ($reason) use (&$rejections) {
|
||||
$rejections[] = $reason;
|
||||
}
|
||||
)->then(
|
||||
function () use (&$results, &$rejections, $count) {
|
||||
if (count($results) !== $count) {
|
||||
throw new AggregateException(
|
||||
'Not enough promises to fulfill count',
|
||||
$rejections
|
||||
);
|
||||
}
|
||||
ksort($results);
|
||||
return array_values($results);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like some(), with 1 as count. However, if the promise fulfills, the
|
||||
* fulfillment value is not an array of 1 but the value directly.
|
||||
*
|
||||
* @param mixed $promises Promises or values.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public static function any($promises)
|
||||
{
|
||||
return self::some(1, $promises)->then(function ($values) {
|
||||
return $values[0];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that is fulfilled when all of the provided promises have
|
||||
* been fulfilled or rejected.
|
||||
*
|
||||
* The returned promise is fulfilled with an array of inspection state arrays.
|
||||
*
|
||||
* @see inspect for the inspection state array format.
|
||||
*
|
||||
* @param mixed $promises Promises or values.
|
||||
*
|
||||
* @return PromiseInterface
|
||||
*/
|
||||
public static function settle($promises)
|
||||
{
|
||||
$results = [];
|
||||
|
||||
return Each::of(
|
||||
$promises,
|
||||
function ($value, $idx) use (&$results) {
|
||||
$results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
|
||||
},
|
||||
function ($reason, $idx) use (&$results) {
|
||||
$results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
|
||||
}
|
||||
)->then(function () use (&$results) {
|
||||
ksort($results);
|
||||
return $results;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user