Rename plugins to libs and update all file references

This commit is contained in:
johnnyq
2026-07-10 13:24:20 -04:00
parent 7ba1571400
commit 8da3a107fb
3410 changed files with 140 additions and 140 deletions

7
libs/vendor/autoload.php vendored Normal file
View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitbadf1d01c367c06fb591106ea3486c30::getLoader();

107
libs/vendor/bin/carbon vendored Executable file
View File

@@ -0,0 +1,107 @@
#!/usr/bin/env php
<?php
/**
* Proxy PHP file generated by Composer
*
* This file includes the referenced bin path (../nesbot/carbon/bin/carbon)
* using a stream wrapper to prevent the shebang from being output on PHP<8
*
* @generated
*/
namespace Composer;
$GLOBALS['_composer_bin_dir'] = __DIR__;
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
if (PHP_VERSION_ID < 80000) {
if (!class_exists('Composer\BinProxyWrapper')) {
/**
* @internal
*/
final class BinProxyWrapper
{
private $handle;
private $position;
private $realpath;
public function stream_open($path, $mode, $options, &$opened_path)
{
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
$opened_path = substr($path, 17);
$this->realpath = realpath($opened_path) ?: $opened_path;
$opened_path = $this->realpath;
$this->handle = fopen($this->realpath, $mode);
$this->position = 0;
return (bool) $this->handle;
}
public function stream_read($count)
{
$data = fread($this->handle, $count);
if ($this->position === 0) {
$data = preg_replace('{^#!.*\r?\n}', '', $data);
}
$this->position += strlen($data);
return $data;
}
public function stream_cast($castAs)
{
return $this->handle;
}
public function stream_close()
{
fclose($this->handle);
}
public function stream_lock($operation)
{
return $operation ? flock($this->handle, $operation) : true;
}
public function stream_tell()
{
return $this->position;
}
public function stream_eof()
{
return feof($this->handle);
}
public function stream_stat()
{
return array();
}
public function stream_set_option($option, $arg1, $arg2)
{
return true;
}
public function url_stat($path, $flags)
{
$path = substr($path, 17);
if (file_exists($path)) {
return stat($path);
}
return false;
}
}
}
if (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper')) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
exit(0);
}
}
include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Carbon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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.

View File

@@ -0,0 +1,14 @@
# carbonphp/carbon-doctrine-types
Types to use Carbon in Doctrine
## Documentation
[Check how to use in the official Carbon documentation](https://carbon.nesbot.com/symfony/)
This package is an externalization of [src/Carbon/Doctrine](https://github.com/briannesbitt/Carbon/tree/2.71.0/src/Carbon/Doctrine)
from `nestbot/carbon` package.
Externalization allows to better deal with different versions of dbal. With
version 4.0 of dbal, it no longer sustainable to be compatible with all version
using a single code.

View File

@@ -0,0 +1,36 @@
{
"name": "carbonphp/carbon-doctrine-types",
"description": "Types to use Carbon in Doctrine",
"type": "library",
"keywords": [
"date",
"time",
"DateTime",
"Carbon",
"Doctrine"
],
"require": {
"php": "^8.1"
},
"require-dev": {
"doctrine/dbal": "^4.0.0",
"nesbot/carbon": "^2.71.0 || ^3.0.0",
"phpunit/phpunit": "^10.3"
},
"conflict": {
"doctrine/dbal": "<4.0.0 || >=5.0.0"
},
"license": "MIT",
"autoload": {
"psr-4": {
"Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
}
},
"authors": [
{
"name": "KyleKatarn",
"email": "kylekatarnls@gmail.com"
}
],
"minimum-stability": "dev"
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Carbon\Doctrine;
use Doctrine\DBAL\Platforms\AbstractPlatform;
interface CarbonDoctrineType
{
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform);
public function convertToPHPValue(mixed $value, AbstractPlatform $platform);
public function convertToDatabaseValue($value, AbstractPlatform $platform);
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Carbon\Doctrine;
class CarbonImmutableType extends DateTimeImmutableType implements CarbonDoctrineType
{
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Carbon\Doctrine;
class CarbonType extends DateTimeType implements CarbonDoctrineType
{
}

View File

@@ -0,0 +1,131 @@
<?php
declare(strict_types=1);
namespace Carbon\Doctrine;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use DateTimeInterface;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\SQLitePlatform;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Types\Exception\InvalidType;
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
use Exception;
/**
* @template T of CarbonInterface
*/
trait CarbonTypeConverter
{
/**
* This property differentiates types installed by carbonphp/carbon-doctrine-types
* from the ones embedded previously in nesbot/carbon source directly.
*
* @readonly
*/
public bool $external = true;
/**
* @return class-string<T>
*/
protected function getCarbonClassName(): string
{
return Carbon::class;
}
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
{
$precision = min(
$fieldDeclaration['precision'] ?? DateTimeDefaultPrecision::get(),
$this->getMaximumPrecision($platform),
);
$type = parent::getSQLDeclaration($fieldDeclaration, $platform);
if (!$precision) {
return $type;
}
if (str_contains($type, '(')) {
return preg_replace('/\(\d+\)/', "($precision)", $type);
}
[$before, $after] = explode(' ', "$type ");
return trim("$before($precision) $after");
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{
if ($value === null) {
return $value;
}
if ($value instanceof DateTimeInterface) {
return $value->format('Y-m-d H:i:s.u');
}
throw InvalidType::new(
$value,
static::class,
['null', 'DateTime', 'Carbon']
);
}
private function doConvertToPHPValue(mixed $value)
{
$class = $this->getCarbonClassName();
if ($value === null || is_a($value, $class)) {
return $value;
}
if ($value instanceof DateTimeInterface) {
return $class::instance($value);
}
$date = null;
$error = null;
try {
$date = $class::parse($value);
} catch (Exception $exception) {
$error = $exception;
}
if (!$date) {
throw ValueNotConvertible::new(
$value,
static::class,
'Y-m-d H:i:s.u or any format supported by '.$class.'::parse()',
$error
);
}
return $date;
}
private function getMaximumPrecision(AbstractPlatform $platform): int
{
if ($platform instanceof DB2Platform) {
return 12;
}
if ($platform instanceof OraclePlatform) {
return 9;
}
if ($platform instanceof SQLServerPlatform || $platform instanceof SQLitePlatform) {
return 3;
}
return 6;
}
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Carbon\Doctrine;
class DateTimeDefaultPrecision
{
private static $precision = 6;
/**
* Change the default Doctrine datetime and datetime_immutable precision.
*
* @param int $precision
*/
public static function set(int $precision): void
{
self::$precision = $precision;
}
/**
* Get the default Doctrine datetime and datetime_immutable precision.
*
* @return int
*/
public static function get(): int
{
return self::$precision;
}
}

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Carbon\Doctrine;
use Carbon\CarbonImmutable;
use DateTimeImmutable;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\VarDateTimeImmutableType;
class DateTimeImmutableType extends VarDateTimeImmutableType implements CarbonDoctrineType
{
/** @use CarbonTypeConverter<CarbonImmutable> */
use CarbonTypeConverter;
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?CarbonImmutable
{
return $this->doConvertToPHPValue($value);
}
/**
* @return class-string<CarbonImmutable>
*/
protected function getCarbonClassName(): string
{
return CarbonImmutable::class;
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Carbon\Doctrine;
use Carbon\Carbon;
use DateTime;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\VarDateTimeType;
class DateTimeType extends VarDateTimeType implements CarbonDoctrineType
{
/** @use CarbonTypeConverter<Carbon> */
use CarbonTypeConverter;
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Carbon
{
return $this->doConvertToPHPValue($value);
}
}

572
libs/vendor/composer/ClassLoader.php vendored Normal file
View File

@@ -0,0 +1,572 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}

View File

@@ -0,0 +1,350 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
}

21
libs/vendor/composer/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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.

View File

@@ -0,0 +1,946 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Carbon\\AbstractTranslator' => $vendorDir . '/nesbot/carbon/src/Carbon/AbstractTranslator.php',
'Carbon\\Callback' => $vendorDir . '/nesbot/carbon/src/Carbon/Callback.php',
'Carbon\\Carbon' => $vendorDir . '/nesbot/carbon/src/Carbon/Carbon.php',
'Carbon\\CarbonConverterInterface' => $vendorDir . '/nesbot/carbon/src/Carbon/CarbonConverterInterface.php',
'Carbon\\CarbonImmutable' => $vendorDir . '/nesbot/carbon/src/Carbon/CarbonImmutable.php',
'Carbon\\CarbonInterface' => $vendorDir . '/nesbot/carbon/src/Carbon/CarbonInterface.php',
'Carbon\\CarbonInterval' => $vendorDir . '/nesbot/carbon/src/Carbon/CarbonInterval.php',
'Carbon\\CarbonPeriod' => $vendorDir . '/nesbot/carbon/src/Carbon/CarbonPeriod.php',
'Carbon\\CarbonPeriodImmutable' => $vendorDir . '/nesbot/carbon/src/Carbon/CarbonPeriodImmutable.php',
'Carbon\\CarbonTimeZone' => $vendorDir . '/nesbot/carbon/src/Carbon/CarbonTimeZone.php',
'Carbon\\Cli\\Invoker' => $vendorDir . '/nesbot/carbon/src/Carbon/Cli/Invoker.php',
'Carbon\\Constants\\DiffOptions' => $vendorDir . '/nesbot/carbon/src/Carbon/Constants/DiffOptions.php',
'Carbon\\Constants\\Format' => $vendorDir . '/nesbot/carbon/src/Carbon/Constants/Format.php',
'Carbon\\Constants\\TranslationOptions' => $vendorDir . '/nesbot/carbon/src/Carbon/Constants/TranslationOptions.php',
'Carbon\\Constants\\UnitValue' => $vendorDir . '/nesbot/carbon/src/Carbon/Constants/UnitValue.php',
'Carbon\\Doctrine\\CarbonDoctrineType' => $vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonDoctrineType.php',
'Carbon\\Doctrine\\CarbonImmutableType' => $vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonImmutableType.php',
'Carbon\\Doctrine\\CarbonType' => $vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonType.php',
'Carbon\\Doctrine\\CarbonTypeConverter' => $vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonTypeConverter.php',
'Carbon\\Doctrine\\DateTimeDefaultPrecision' => $vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeDefaultPrecision.php',
'Carbon\\Doctrine\\DateTimeImmutableType' => $vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeImmutableType.php',
'Carbon\\Doctrine\\DateTimeType' => $vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeType.php',
'Carbon\\Exceptions\\BadComparisonUnitException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/BadComparisonUnitException.php',
'Carbon\\Exceptions\\BadFluentConstructorException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/BadFluentConstructorException.php',
'Carbon\\Exceptions\\BadFluentSetterException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/BadFluentSetterException.php',
'Carbon\\Exceptions\\BadMethodCallException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/BadMethodCallException.php',
'Carbon\\Exceptions\\EndLessPeriodException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/EndLessPeriodException.php',
'Carbon\\Exceptions\\Exception' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/Exception.php',
'Carbon\\Exceptions\\ImmutableException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/ImmutableException.php',
'Carbon\\Exceptions\\InvalidArgumentException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/InvalidArgumentException.php',
'Carbon\\Exceptions\\InvalidCastException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/InvalidCastException.php',
'Carbon\\Exceptions\\InvalidDateException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/InvalidDateException.php',
'Carbon\\Exceptions\\InvalidFormatException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/InvalidFormatException.php',
'Carbon\\Exceptions\\InvalidIntervalException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/InvalidIntervalException.php',
'Carbon\\Exceptions\\InvalidPeriodDateException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodDateException.php',
'Carbon\\Exceptions\\InvalidPeriodParameterException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/InvalidPeriodParameterException.php',
'Carbon\\Exceptions\\InvalidTimeZoneException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/InvalidTimeZoneException.php',
'Carbon\\Exceptions\\InvalidTypeException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/InvalidTypeException.php',
'Carbon\\Exceptions\\NotACarbonClassException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/NotACarbonClassException.php',
'Carbon\\Exceptions\\NotAPeriodException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/NotAPeriodException.php',
'Carbon\\Exceptions\\NotLocaleAwareException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/NotLocaleAwareException.php',
'Carbon\\Exceptions\\OutOfRangeException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/OutOfRangeException.php',
'Carbon\\Exceptions\\ParseErrorException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/ParseErrorException.php',
'Carbon\\Exceptions\\RuntimeException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/RuntimeException.php',
'Carbon\\Exceptions\\UnitException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/UnitException.php',
'Carbon\\Exceptions\\UnitNotConfiguredException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/UnitNotConfiguredException.php',
'Carbon\\Exceptions\\UnknownGetterException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/UnknownGetterException.php',
'Carbon\\Exceptions\\UnknownMethodException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/UnknownMethodException.php',
'Carbon\\Exceptions\\UnknownSetterException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/UnknownSetterException.php',
'Carbon\\Exceptions\\UnknownUnitException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/UnknownUnitException.php',
'Carbon\\Exceptions\\UnreachableException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/UnreachableException.php',
'Carbon\\Exceptions\\UnsupportedUnitException' => $vendorDir . '/nesbot/carbon/src/Carbon/Exceptions/UnsupportedUnitException.php',
'Carbon\\Factory' => $vendorDir . '/nesbot/carbon/src/Carbon/Factory.php',
'Carbon\\FactoryImmutable' => $vendorDir . '/nesbot/carbon/src/Carbon/FactoryImmutable.php',
'Carbon\\Language' => $vendorDir . '/nesbot/carbon/src/Carbon/Language.php',
'Carbon\\Laravel\\ServiceProvider' => $vendorDir . '/nesbot/carbon/src/Carbon/Laravel/ServiceProvider.php',
'Carbon\\MessageFormatter\\MessageFormatterMapper' => $vendorDir . '/nesbot/carbon/src/Carbon/MessageFormatter/MessageFormatterMapper.php',
'Carbon\\Month' => $vendorDir . '/nesbot/carbon/src/Carbon/Month.php',
'Carbon\\OverflowMode' => $vendorDir . '/nesbot/carbon/src/Carbon/OverflowMode.php',
'Carbon\\PHPStan\\MacroExtension' => $vendorDir . '/nesbot/carbon/src/Carbon/PHPStan/MacroExtension.php',
'Carbon\\PHPStan\\MacroMethodReflection' => $vendorDir . '/nesbot/carbon/src/Carbon/PHPStan/MacroMethodReflection.php',
'Carbon\\Traits\\Boundaries' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Boundaries.php',
'Carbon\\Traits\\Cast' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Cast.php',
'Carbon\\Traits\\Comparison' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Comparison.php',
'Carbon\\Traits\\Converter' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Converter.php',
'Carbon\\Traits\\Creator' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Creator.php',
'Carbon\\Traits\\Date' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Date.php',
'Carbon\\Traits\\DeprecatedPeriodProperties' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/DeprecatedPeriodProperties.php',
'Carbon\\Traits\\Difference' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Difference.php',
'Carbon\\Traits\\IntervalRounding' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/IntervalRounding.php',
'Carbon\\Traits\\IntervalStep' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/IntervalStep.php',
'Carbon\\Traits\\LocalFactory' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/LocalFactory.php',
'Carbon\\Traits\\Localization' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Localization.php',
'Carbon\\Traits\\Macro' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Macro.php',
'Carbon\\Traits\\MagicParameter' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/MagicParameter.php',
'Carbon\\Traits\\Mixin' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Mixin.php',
'Carbon\\Traits\\Modifiers' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Modifiers.php',
'Carbon\\Traits\\Mutability' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Mutability.php',
'Carbon\\Traits\\ObjectInitialisation' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/ObjectInitialisation.php',
'Carbon\\Traits\\Options' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Options.php',
'Carbon\\Traits\\Rounding' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Rounding.php',
'Carbon\\Traits\\Serialization' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Serialization.php',
'Carbon\\Traits\\StaticLocalization' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/StaticLocalization.php',
'Carbon\\Traits\\StaticOptions' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/StaticOptions.php',
'Carbon\\Traits\\Test' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Test.php',
'Carbon\\Traits\\Timestamp' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Timestamp.php',
'Carbon\\Traits\\ToStringFormat' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/ToStringFormat.php',
'Carbon\\Traits\\Units' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Units.php',
'Carbon\\Traits\\Week' => $vendorDir . '/nesbot/carbon/src/Carbon/Traits/Week.php',
'Carbon\\Translator' => $vendorDir . '/nesbot/carbon/src/Carbon/Translator.php',
'Carbon\\TranslatorImmutable' => $vendorDir . '/nesbot/carbon/src/Carbon/TranslatorImmutable.php',
'Carbon\\TranslatorStrongTypeInterface' => $vendorDir . '/nesbot/carbon/src/Carbon/TranslatorStrongTypeInterface.php',
'Carbon\\Unit' => $vendorDir . '/nesbot/carbon/src/Carbon/Unit.php',
'Carbon\\WeekDay' => $vendorDir . '/nesbot/carbon/src/Carbon/WeekDay.php',
'Carbon\\WrapperClock' => $vendorDir . '/nesbot/carbon/src/Carbon/WrapperClock.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'DI\\Attribute\\Inject' => $vendorDir . '/php-di/php-di/src/Attribute/Inject.php',
'DI\\Attribute\\Injectable' => $vendorDir . '/php-di/php-di/src/Attribute/Injectable.php',
'DI\\CompiledContainer' => $vendorDir . '/php-di/php-di/src/CompiledContainer.php',
'DI\\Compiler\\Compiler' => $vendorDir . '/php-di/php-di/src/Compiler/Compiler.php',
'DI\\Compiler\\ObjectCreationCompiler' => $vendorDir . '/php-di/php-di/src/Compiler/ObjectCreationCompiler.php',
'DI\\Compiler\\RequestedEntryHolder' => $vendorDir . '/php-di/php-di/src/Compiler/RequestedEntryHolder.php',
'DI\\Container' => $vendorDir . '/php-di/php-di/src/Container.php',
'DI\\ContainerBuilder' => $vendorDir . '/php-di/php-di/src/ContainerBuilder.php',
'DI\\Definition\\ArrayDefinition' => $vendorDir . '/php-di/php-di/src/Definition/ArrayDefinition.php',
'DI\\Definition\\ArrayDefinitionExtension' => $vendorDir . '/php-di/php-di/src/Definition/ArrayDefinitionExtension.php',
'DI\\Definition\\AutowireDefinition' => $vendorDir . '/php-di/php-di/src/Definition/AutowireDefinition.php',
'DI\\Definition\\DecoratorDefinition' => $vendorDir . '/php-di/php-di/src/Definition/DecoratorDefinition.php',
'DI\\Definition\\Definition' => $vendorDir . '/php-di/php-di/src/Definition/Definition.php',
'DI\\Definition\\Dumper\\ObjectDefinitionDumper' => $vendorDir . '/php-di/php-di/src/Definition/Dumper/ObjectDefinitionDumper.php',
'DI\\Definition\\EnvironmentVariableDefinition' => $vendorDir . '/php-di/php-di/src/Definition/EnvironmentVariableDefinition.php',
'DI\\Definition\\Exception\\InvalidAttribute' => $vendorDir . '/php-di/php-di/src/Definition/Exception/InvalidAttribute.php',
'DI\\Definition\\Exception\\InvalidDefinition' => $vendorDir . '/php-di/php-di/src/Definition/Exception/InvalidDefinition.php',
'DI\\Definition\\ExtendsPreviousDefinition' => $vendorDir . '/php-di/php-di/src/Definition/ExtendsPreviousDefinition.php',
'DI\\Definition\\FactoryDefinition' => $vendorDir . '/php-di/php-di/src/Definition/FactoryDefinition.php',
'DI\\Definition\\Helper\\AutowireDefinitionHelper' => $vendorDir . '/php-di/php-di/src/Definition/Helper/AutowireDefinitionHelper.php',
'DI\\Definition\\Helper\\CreateDefinitionHelper' => $vendorDir . '/php-di/php-di/src/Definition/Helper/CreateDefinitionHelper.php',
'DI\\Definition\\Helper\\DefinitionHelper' => $vendorDir . '/php-di/php-di/src/Definition/Helper/DefinitionHelper.php',
'DI\\Definition\\Helper\\FactoryDefinitionHelper' => $vendorDir . '/php-di/php-di/src/Definition/Helper/FactoryDefinitionHelper.php',
'DI\\Definition\\InstanceDefinition' => $vendorDir . '/php-di/php-di/src/Definition/InstanceDefinition.php',
'DI\\Definition\\ObjectDefinition' => $vendorDir . '/php-di/php-di/src/Definition/ObjectDefinition.php',
'DI\\Definition\\ObjectDefinition\\MethodInjection' => $vendorDir . '/php-di/php-di/src/Definition/ObjectDefinition/MethodInjection.php',
'DI\\Definition\\ObjectDefinition\\PropertyInjection' => $vendorDir . '/php-di/php-di/src/Definition/ObjectDefinition/PropertyInjection.php',
'DI\\Definition\\Reference' => $vendorDir . '/php-di/php-di/src/Definition/Reference.php',
'DI\\Definition\\Resolver\\ArrayResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/ArrayResolver.php',
'DI\\Definition\\Resolver\\DecoratorResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/DecoratorResolver.php',
'DI\\Definition\\Resolver\\DefinitionResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/DefinitionResolver.php',
'DI\\Definition\\Resolver\\EnvironmentVariableResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/EnvironmentVariableResolver.php',
'DI\\Definition\\Resolver\\FactoryResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/FactoryResolver.php',
'DI\\Definition\\Resolver\\InstanceInjector' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/InstanceInjector.php',
'DI\\Definition\\Resolver\\ObjectCreator' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/ObjectCreator.php',
'DI\\Definition\\Resolver\\ParameterResolver' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/ParameterResolver.php',
'DI\\Definition\\Resolver\\ResolverDispatcher' => $vendorDir . '/php-di/php-di/src/Definition/Resolver/ResolverDispatcher.php',
'DI\\Definition\\SelfResolvingDefinition' => $vendorDir . '/php-di/php-di/src/Definition/SelfResolvingDefinition.php',
'DI\\Definition\\Source\\AttributeBasedAutowiring' => $vendorDir . '/php-di/php-di/src/Definition/Source/AttributeBasedAutowiring.php',
'DI\\Definition\\Source\\Autowiring' => $vendorDir . '/php-di/php-di/src/Definition/Source/Autowiring.php',
'DI\\Definition\\Source\\DefinitionArray' => $vendorDir . '/php-di/php-di/src/Definition/Source/DefinitionArray.php',
'DI\\Definition\\Source\\DefinitionFile' => $vendorDir . '/php-di/php-di/src/Definition/Source/DefinitionFile.php',
'DI\\Definition\\Source\\DefinitionNormalizer' => $vendorDir . '/php-di/php-di/src/Definition/Source/DefinitionNormalizer.php',
'DI\\Definition\\Source\\DefinitionSource' => $vendorDir . '/php-di/php-di/src/Definition/Source/DefinitionSource.php',
'DI\\Definition\\Source\\MutableDefinitionSource' => $vendorDir . '/php-di/php-di/src/Definition/Source/MutableDefinitionSource.php',
'DI\\Definition\\Source\\NoAutowiring' => $vendorDir . '/php-di/php-di/src/Definition/Source/NoAutowiring.php',
'DI\\Definition\\Source\\ReflectionBasedAutowiring' => $vendorDir . '/php-di/php-di/src/Definition/Source/ReflectionBasedAutowiring.php',
'DI\\Definition\\Source\\SourceCache' => $vendorDir . '/php-di/php-di/src/Definition/Source/SourceCache.php',
'DI\\Definition\\Source\\SourceChain' => $vendorDir . '/php-di/php-di/src/Definition/Source/SourceChain.php',
'DI\\Definition\\StringDefinition' => $vendorDir . '/php-di/php-di/src/Definition/StringDefinition.php',
'DI\\Definition\\ValueDefinition' => $vendorDir . '/php-di/php-di/src/Definition/ValueDefinition.php',
'DI\\DependencyException' => $vendorDir . '/php-di/php-di/src/DependencyException.php',
'DI\\FactoryInterface' => $vendorDir . '/php-di/php-di/src/FactoryInterface.php',
'DI\\Factory\\RequestedEntry' => $vendorDir . '/php-di/php-di/src/Factory/RequestedEntry.php',
'DI\\Invoker\\DefinitionParameterResolver' => $vendorDir . '/php-di/php-di/src/Invoker/DefinitionParameterResolver.php',
'DI\\Invoker\\FactoryParameterResolver' => $vendorDir . '/php-di/php-di/src/Invoker/FactoryParameterResolver.php',
'DI\\NotFoundException' => $vendorDir . '/php-di/php-di/src/NotFoundException.php',
'DI\\Proxy\\NativeProxyFactory' => $vendorDir . '/php-di/php-di/src/Proxy/NativeProxyFactory.php',
'DI\\Proxy\\ProxyFactory' => $vendorDir . '/php-di/php-di/src/Proxy/ProxyFactory.php',
'DI\\Proxy\\ProxyFactoryInterface' => $vendorDir . '/php-di/php-di/src/Proxy/ProxyFactoryInterface.php',
'DateError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
'DateException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
'DateInvalidOperationException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
'DateInvalidTimeZoneException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidTimeZoneException.php',
'DateMalformedIntervalStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedIntervalStringException.php',
'DateMalformedPeriodStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedPeriodStringException.php',
'DateMalformedStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php',
'DateObjectError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php',
'DateRangeError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php',
'DelayedTargetValidation' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/DelayedTargetValidation.php',
'Deprecated' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Deprecated.php',
'DirectoryTree\\ImapEngine\\Address' => $vendorDir . '/directorytree/imapengine/src/Address.php',
'DirectoryTree\\ImapEngine\\Attachment' => $vendorDir . '/directorytree/imapengine/src/Attachment.php',
'DirectoryTree\\ImapEngine\\BodyStructureCollection' => $vendorDir . '/directorytree/imapengine/src/BodyStructureCollection.php',
'DirectoryTree\\ImapEngine\\BodyStructurePart' => $vendorDir . '/directorytree/imapengine/src/BodyStructurePart.php',
'DirectoryTree\\ImapEngine\\Collections\\FolderCollection' => $vendorDir . '/directorytree/imapengine/src/Collections/FolderCollection.php',
'DirectoryTree\\ImapEngine\\Collections\\MessageCollection' => $vendorDir . '/directorytree/imapengine/src/Collections/MessageCollection.php',
'DirectoryTree\\ImapEngine\\Collections\\PaginatedCollection' => $vendorDir . '/directorytree/imapengine/src/Collections/PaginatedCollection.php',
'DirectoryTree\\ImapEngine\\Collections\\ResponseCollection' => $vendorDir . '/directorytree/imapengine/src/Collections/ResponseCollection.php',
'DirectoryTree\\ImapEngine\\ComparesFolders' => $vendorDir . '/directorytree/imapengine/src/ComparesFolders.php',
'DirectoryTree\\ImapEngine\\Connection\\ConnectionInterface' => $vendorDir . '/directorytree/imapengine/src/Connection/ConnectionInterface.php',
'DirectoryTree\\ImapEngine\\Connection\\ImapCommand' => $vendorDir . '/directorytree/imapengine/src/Connection/ImapCommand.php',
'DirectoryTree\\ImapEngine\\Connection\\ImapConnection' => $vendorDir . '/directorytree/imapengine/src/Connection/ImapConnection.php',
'DirectoryTree\\ImapEngine\\Connection\\ImapParser' => $vendorDir . '/directorytree/imapengine/src/Connection/ImapParser.php',
'DirectoryTree\\ImapEngine\\Connection\\ImapQueryBuilder' => $vendorDir . '/directorytree/imapengine/src/Connection/ImapQueryBuilder.php',
'DirectoryTree\\ImapEngine\\Connection\\ImapTokenizer' => $vendorDir . '/directorytree/imapengine/src/Connection/ImapTokenizer.php',
'DirectoryTree\\ImapEngine\\Connection\\Loggers\\EchoLogger' => $vendorDir . '/directorytree/imapengine/src/Connection/Loggers/EchoLogger.php',
'DirectoryTree\\ImapEngine\\Connection\\Loggers\\FileLogger' => $vendorDir . '/directorytree/imapengine/src/Connection/Loggers/FileLogger.php',
'DirectoryTree\\ImapEngine\\Connection\\Loggers\\Logger' => $vendorDir . '/directorytree/imapengine/src/Connection/Loggers/Logger.php',
'DirectoryTree\\ImapEngine\\Connection\\Loggers\\LoggerInterface' => $vendorDir . '/directorytree/imapengine/src/Connection/Loggers/LoggerInterface.php',
'DirectoryTree\\ImapEngine\\Connection\\Loggers\\RayLogger' => $vendorDir . '/directorytree/imapengine/src/Connection/Loggers/RayLogger.php',
'DirectoryTree\\ImapEngine\\Connection\\RawQueryValue' => $vendorDir . '/directorytree/imapengine/src/Connection/RawQueryValue.php',
'DirectoryTree\\ImapEngine\\Connection\\Responses\\ContinuationResponse' => $vendorDir . '/directorytree/imapengine/src/Connection/Responses/ContinuationResponse.php',
'DirectoryTree\\ImapEngine\\Connection\\Responses\\Data\\Data' => $vendorDir . '/directorytree/imapengine/src/Connection/Responses/Data/Data.php',
'DirectoryTree\\ImapEngine\\Connection\\Responses\\Data\\ListData' => $vendorDir . '/directorytree/imapengine/src/Connection/Responses/Data/ListData.php',
'DirectoryTree\\ImapEngine\\Connection\\Responses\\Data\\ResponseCodeData' => $vendorDir . '/directorytree/imapengine/src/Connection/Responses/Data/ResponseCodeData.php',
'DirectoryTree\\ImapEngine\\Connection\\Responses\\HasTokens' => $vendorDir . '/directorytree/imapengine/src/Connection/Responses/HasTokens.php',
'DirectoryTree\\ImapEngine\\Connection\\Responses\\MessageResponseParser' => $vendorDir . '/directorytree/imapengine/src/Connection/Responses/MessageResponseParser.php',
'DirectoryTree\\ImapEngine\\Connection\\Responses\\Response' => $vendorDir . '/directorytree/imapengine/src/Connection/Responses/Response.php',
'DirectoryTree\\ImapEngine\\Connection\\Responses\\TaggedResponse' => $vendorDir . '/directorytree/imapengine/src/Connection/Responses/TaggedResponse.php',
'DirectoryTree\\ImapEngine\\Connection\\Responses\\UntaggedResponse' => $vendorDir . '/directorytree/imapengine/src/Connection/Responses/UntaggedResponse.php',
'DirectoryTree\\ImapEngine\\Connection\\Result' => $vendorDir . '/directorytree/imapengine/src/Connection/Result.php',
'DirectoryTree\\ImapEngine\\Connection\\Streams\\FakeStream' => $vendorDir . '/directorytree/imapengine/src/Connection/Streams/FakeStream.php',
'DirectoryTree\\ImapEngine\\Connection\\Streams\\ImapStream' => $vendorDir . '/directorytree/imapengine/src/Connection/Streams/ImapStream.php',
'DirectoryTree\\ImapEngine\\Connection\\Streams\\StreamInterface' => $vendorDir . '/directorytree/imapengine/src/Connection/Streams/StreamInterface.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\Atom' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/Atom.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\Crlf' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/Crlf.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\EmailAddress' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/EmailAddress.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\ListClose' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/ListClose.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\ListOpen' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/ListOpen.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\Literal' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/Literal.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\Nil' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/Nil.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\Number' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/Number.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\QuotedString' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/QuotedString.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\ResponseCodeClose' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/ResponseCodeClose.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\ResponseCodeOpen' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/ResponseCodeOpen.php',
'DirectoryTree\\ImapEngine\\Connection\\Tokens\\Token' => $vendorDir . '/directorytree/imapengine/src/Connection/Tokens/Token.php',
'DirectoryTree\\ImapEngine\\ContentDisposition' => $vendorDir . '/directorytree/imapengine/src/ContentDisposition.php',
'DirectoryTree\\ImapEngine\\DraftMessage' => $vendorDir . '/directorytree/imapengine/src/DraftMessage.php',
'DirectoryTree\\ImapEngine\\Enums\\ContentDispositionType' => $vendorDir . '/directorytree/imapengine/src/Enums/ContentDispositionType.php',
'DirectoryTree\\ImapEngine\\Enums\\ImapFetchIdentifier' => $vendorDir . '/directorytree/imapengine/src/Enums/ImapFetchIdentifier.php',
'DirectoryTree\\ImapEngine\\Enums\\ImapFlag' => $vendorDir . '/directorytree/imapengine/src/Enums/ImapFlag.php',
'DirectoryTree\\ImapEngine\\Enums\\ImapSearchKey' => $vendorDir . '/directorytree/imapengine/src/Enums/ImapSearchKey.php',
'DirectoryTree\\ImapEngine\\Enums\\ImapSortKey' => $vendorDir . '/directorytree/imapengine/src/Enums/ImapSortKey.php',
'DirectoryTree\\ImapEngine\\Exceptions\\Exception' => $vendorDir . '/directorytree/imapengine/src/Exceptions/Exception.php',
'DirectoryTree\\ImapEngine\\Exceptions\\ImapCapabilityException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/ImapCapabilityException.php',
'DirectoryTree\\ImapEngine\\Exceptions\\ImapCommandException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/ImapCommandException.php',
'DirectoryTree\\ImapEngine\\Exceptions\\ImapConnectionClosedException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/ImapConnectionClosedException.php',
'DirectoryTree\\ImapEngine\\Exceptions\\ImapConnectionException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/ImapConnectionException.php',
'DirectoryTree\\ImapEngine\\Exceptions\\ImapConnectionFailedException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/ImapConnectionFailedException.php',
'DirectoryTree\\ImapEngine\\Exceptions\\ImapConnectionTimedOutException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/ImapConnectionTimedOutException.php',
'DirectoryTree\\ImapEngine\\Exceptions\\ImapParserException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/ImapParserException.php',
'DirectoryTree\\ImapEngine\\Exceptions\\ImapResponseException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/ImapResponseException.php',
'DirectoryTree\\ImapEngine\\Exceptions\\ImapStreamException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/ImapStreamException.php',
'DirectoryTree\\ImapEngine\\Exceptions\\RuntimeException' => $vendorDir . '/directorytree/imapengine/src/Exceptions/RuntimeException.php',
'DirectoryTree\\ImapEngine\\FileMessage' => $vendorDir . '/directorytree/imapengine/src/FileMessage.php',
'DirectoryTree\\ImapEngine\\FlaggableInterface' => $vendorDir . '/directorytree/imapengine/src/FlaggableInterface.php',
'DirectoryTree\\ImapEngine\\Folder' => $vendorDir . '/directorytree/imapengine/src/Folder.php',
'DirectoryTree\\ImapEngine\\FolderInterface' => $vendorDir . '/directorytree/imapengine/src/FolderInterface.php',
'DirectoryTree\\ImapEngine\\FolderRepository' => $vendorDir . '/directorytree/imapengine/src/FolderRepository.php',
'DirectoryTree\\ImapEngine\\FolderRepositoryInterface' => $vendorDir . '/directorytree/imapengine/src/FolderRepositoryInterface.php',
'DirectoryTree\\ImapEngine\\HasFlags' => $vendorDir . '/directorytree/imapengine/src/HasFlags.php',
'DirectoryTree\\ImapEngine\\HasMessageAccessors' => $vendorDir . '/directorytree/imapengine/src/HasMessageAccessors.php',
'DirectoryTree\\ImapEngine\\HasParsedMessage' => $vendorDir . '/directorytree/imapengine/src/HasParsedMessage.php',
'DirectoryTree\\ImapEngine\\Idle' => $vendorDir . '/directorytree/imapengine/src/Idle.php',
'DirectoryTree\\ImapEngine\\Mailbox' => $vendorDir . '/directorytree/imapengine/src/Mailbox.php',
'DirectoryTree\\ImapEngine\\MailboxInterface' => $vendorDir . '/directorytree/imapengine/src/MailboxInterface.php',
'DirectoryTree\\ImapEngine\\Mbox' => $vendorDir . '/directorytree/imapengine/src/Mbox.php',
'DirectoryTree\\ImapEngine\\Message' => $vendorDir . '/directorytree/imapengine/src/Message.php',
'DirectoryTree\\ImapEngine\\MessageInterface' => $vendorDir . '/directorytree/imapengine/src/MessageInterface.php',
'DirectoryTree\\ImapEngine\\MessageParser' => $vendorDir . '/directorytree/imapengine/src/MessageParser.php',
'DirectoryTree\\ImapEngine\\MessageQuery' => $vendorDir . '/directorytree/imapengine/src/MessageQuery.php',
'DirectoryTree\\ImapEngine\\MessageQueryInterface' => $vendorDir . '/directorytree/imapengine/src/MessageQueryInterface.php',
'DirectoryTree\\ImapEngine\\Pagination\\LengthAwarePaginator' => $vendorDir . '/directorytree/imapengine/src/Pagination/LengthAwarePaginator.php',
'DirectoryTree\\ImapEngine\\Poll' => $vendorDir . '/directorytree/imapengine/src/Poll.php',
'DirectoryTree\\ImapEngine\\QueriesMessages' => $vendorDir . '/directorytree/imapengine/src/QueriesMessages.php',
'DirectoryTree\\ImapEngine\\Support\\BodyPartDecoder' => $vendorDir . '/directorytree/imapengine/src/Support/BodyPartDecoder.php',
'DirectoryTree\\ImapEngine\\Support\\ForwardsCalls' => $vendorDir . '/directorytree/imapengine/src/Support/ForwardsCalls.php',
'DirectoryTree\\ImapEngine\\Support\\LazyBodyPartStream' => $vendorDir . '/directorytree/imapengine/src/Support/LazyBodyPartStream.php',
'DirectoryTree\\ImapEngine\\Support\\MimeMessage' => $vendorDir . '/directorytree/imapengine/src/Support/MimeMessage.php',
'DirectoryTree\\ImapEngine\\Support\\Str' => $vendorDir . '/directorytree/imapengine/src/Support/Str.php',
'DirectoryTree\\ImapEngine\\Testing\\FakeFolder' => $vendorDir . '/directorytree/imapengine/src/Testing/FakeFolder.php',
'DirectoryTree\\ImapEngine\\Testing\\FakeFolderRepository' => $vendorDir . '/directorytree/imapengine/src/Testing/FakeFolderRepository.php',
'DirectoryTree\\ImapEngine\\Testing\\FakeMailbox' => $vendorDir . '/directorytree/imapengine/src/Testing/FakeMailbox.php',
'DirectoryTree\\ImapEngine\\Testing\\FakeMessage' => $vendorDir . '/directorytree/imapengine/src/Testing/FakeMessage.php',
'DirectoryTree\\ImapEngine\\Testing\\FakeMessageQuery' => $vendorDir . '/directorytree/imapengine/src/Testing/FakeMessageQuery.php',
'Doctrine\\Common\\Lexer\\AbstractLexer' => $vendorDir . '/doctrine/lexer/src/AbstractLexer.php',
'Doctrine\\Common\\Lexer\\Token' => $vendorDir . '/doctrine/lexer/src/Token.php',
'Egulias\\EmailValidator\\EmailLexer' => $vendorDir . '/egulias/email-validator/src/EmailLexer.php',
'Egulias\\EmailValidator\\EmailParser' => $vendorDir . '/egulias/email-validator/src/EmailParser.php',
'Egulias\\EmailValidator\\EmailValidator' => $vendorDir . '/egulias/email-validator/src/EmailValidator.php',
'Egulias\\EmailValidator\\MessageIDParser' => $vendorDir . '/egulias/email-validator/src/MessageIDParser.php',
'Egulias\\EmailValidator\\Parser' => $vendorDir . '/egulias/email-validator/src/Parser.php',
'Egulias\\EmailValidator\\Parser\\Comment' => $vendorDir . '/egulias/email-validator/src/Parser/Comment.php',
'Egulias\\EmailValidator\\Parser\\CommentStrategy\\CommentStrategy' => $vendorDir . '/egulias/email-validator/src/Parser/CommentStrategy/CommentStrategy.php',
'Egulias\\EmailValidator\\Parser\\CommentStrategy\\DomainComment' => $vendorDir . '/egulias/email-validator/src/Parser/CommentStrategy/DomainComment.php',
'Egulias\\EmailValidator\\Parser\\CommentStrategy\\LocalComment' => $vendorDir . '/egulias/email-validator/src/Parser/CommentStrategy/LocalComment.php',
'Egulias\\EmailValidator\\Parser\\DomainLiteral' => $vendorDir . '/egulias/email-validator/src/Parser/DomainLiteral.php',
'Egulias\\EmailValidator\\Parser\\DomainPart' => $vendorDir . '/egulias/email-validator/src/Parser/DomainPart.php',
'Egulias\\EmailValidator\\Parser\\DoubleQuote' => $vendorDir . '/egulias/email-validator/src/Parser/DoubleQuote.php',
'Egulias\\EmailValidator\\Parser\\FoldingWhiteSpace' => $vendorDir . '/egulias/email-validator/src/Parser/FoldingWhiteSpace.php',
'Egulias\\EmailValidator\\Parser\\IDLeftPart' => $vendorDir . '/egulias/email-validator/src/Parser/IDLeftPart.php',
'Egulias\\EmailValidator\\Parser\\IDRightPart' => $vendorDir . '/egulias/email-validator/src/Parser/IDRightPart.php',
'Egulias\\EmailValidator\\Parser\\LocalPart' => $vendorDir . '/egulias/email-validator/src/Parser/LocalPart.php',
'Egulias\\EmailValidator\\Parser\\PartParser' => $vendorDir . '/egulias/email-validator/src/Parser/PartParser.php',
'Egulias\\EmailValidator\\Result\\InvalidEmail' => $vendorDir . '/egulias/email-validator/src/Result/InvalidEmail.php',
'Egulias\\EmailValidator\\Result\\MultipleErrors' => $vendorDir . '/egulias/email-validator/src/Result/MultipleErrors.php',
'Egulias\\EmailValidator\\Result\\Reason\\AtextAfterCFWS' => $vendorDir . '/egulias/email-validator/src/Result/Reason/AtextAfterCFWS.php',
'Egulias\\EmailValidator\\Result\\Reason\\CRLFAtTheEnd' => $vendorDir . '/egulias/email-validator/src/Result/Reason/CRLFAtTheEnd.php',
'Egulias\\EmailValidator\\Result\\Reason\\CRLFX2' => $vendorDir . '/egulias/email-validator/src/Result/Reason/CRLFX2.php',
'Egulias\\EmailValidator\\Result\\Reason\\CRNoLF' => $vendorDir . '/egulias/email-validator/src/Result/Reason/CRNoLF.php',
'Egulias\\EmailValidator\\Result\\Reason\\CharNotAllowed' => $vendorDir . '/egulias/email-validator/src/Result/Reason/CharNotAllowed.php',
'Egulias\\EmailValidator\\Result\\Reason\\CommaInDomain' => $vendorDir . '/egulias/email-validator/src/Result/Reason/CommaInDomain.php',
'Egulias\\EmailValidator\\Result\\Reason\\CommentsInIDRight' => $vendorDir . '/egulias/email-validator/src/Result/Reason/CommentsInIDRight.php',
'Egulias\\EmailValidator\\Result\\Reason\\ConsecutiveAt' => $vendorDir . '/egulias/email-validator/src/Result/Reason/ConsecutiveAt.php',
'Egulias\\EmailValidator\\Result\\Reason\\ConsecutiveDot' => $vendorDir . '/egulias/email-validator/src/Result/Reason/ConsecutiveDot.php',
'Egulias\\EmailValidator\\Result\\Reason\\DetailedReason' => $vendorDir . '/egulias/email-validator/src/Result/Reason/DetailedReason.php',
'Egulias\\EmailValidator\\Result\\Reason\\DomainAcceptsNoMail' => $vendorDir . '/egulias/email-validator/src/Result/Reason/DomainAcceptsNoMail.php',
'Egulias\\EmailValidator\\Result\\Reason\\DomainHyphened' => $vendorDir . '/egulias/email-validator/src/Result/Reason/DomainHyphened.php',
'Egulias\\EmailValidator\\Result\\Reason\\DomainTooLong' => $vendorDir . '/egulias/email-validator/src/Result/Reason/DomainTooLong.php',
'Egulias\\EmailValidator\\Result\\Reason\\DotAtEnd' => $vendorDir . '/egulias/email-validator/src/Result/Reason/DotAtEnd.php',
'Egulias\\EmailValidator\\Result\\Reason\\DotAtStart' => $vendorDir . '/egulias/email-validator/src/Result/Reason/DotAtStart.php',
'Egulias\\EmailValidator\\Result\\Reason\\EmptyReason' => $vendorDir . '/egulias/email-validator/src/Result/Reason/EmptyReason.php',
'Egulias\\EmailValidator\\Result\\Reason\\ExceptionFound' => $vendorDir . '/egulias/email-validator/src/Result/Reason/ExceptionFound.php',
'Egulias\\EmailValidator\\Result\\Reason\\ExpectingATEXT' => $vendorDir . '/egulias/email-validator/src/Result/Reason/ExpectingATEXT.php',
'Egulias\\EmailValidator\\Result\\Reason\\ExpectingCTEXT' => $vendorDir . '/egulias/email-validator/src/Result/Reason/ExpectingCTEXT.php',
'Egulias\\EmailValidator\\Result\\Reason\\ExpectingDTEXT' => $vendorDir . '/egulias/email-validator/src/Result/Reason/ExpectingDTEXT.php',
'Egulias\\EmailValidator\\Result\\Reason\\ExpectingDomainLiteralClose' => $vendorDir . '/egulias/email-validator/src/Result/Reason/ExpectingDomainLiteralClose.php',
'Egulias\\EmailValidator\\Result\\Reason\\LabelTooLong' => $vendorDir . '/egulias/email-validator/src/Result/Reason/LabelTooLong.php',
'Egulias\\EmailValidator\\Result\\Reason\\LocalOrReservedDomain' => $vendorDir . '/egulias/email-validator/src/Result/Reason/LocalOrReservedDomain.php',
'Egulias\\EmailValidator\\Result\\Reason\\NoDNSRecord' => $vendorDir . '/egulias/email-validator/src/Result/Reason/NoDNSRecord.php',
'Egulias\\EmailValidator\\Result\\Reason\\NoDomainPart' => $vendorDir . '/egulias/email-validator/src/Result/Reason/NoDomainPart.php',
'Egulias\\EmailValidator\\Result\\Reason\\NoLocalPart' => $vendorDir . '/egulias/email-validator/src/Result/Reason/NoLocalPart.php',
'Egulias\\EmailValidator\\Result\\Reason\\RFCWarnings' => $vendorDir . '/egulias/email-validator/src/Result/Reason/RFCWarnings.php',
'Egulias\\EmailValidator\\Result\\Reason\\Reason' => $vendorDir . '/egulias/email-validator/src/Result/Reason/Reason.php',
'Egulias\\EmailValidator\\Result\\Reason\\SpoofEmail' => $vendorDir . '/egulias/email-validator/src/Result/Reason/SpoofEmail.php',
'Egulias\\EmailValidator\\Result\\Reason\\UnOpenedComment' => $vendorDir . '/egulias/email-validator/src/Result/Reason/UnOpenedComment.php',
'Egulias\\EmailValidator\\Result\\Reason\\UnableToGetDNSRecord' => $vendorDir . '/egulias/email-validator/src/Result/Reason/UnableToGetDNSRecord.php',
'Egulias\\EmailValidator\\Result\\Reason\\UnclosedComment' => $vendorDir . '/egulias/email-validator/src/Result/Reason/UnclosedComment.php',
'Egulias\\EmailValidator\\Result\\Reason\\UnclosedQuotedString' => $vendorDir . '/egulias/email-validator/src/Result/Reason/UnclosedQuotedString.php',
'Egulias\\EmailValidator\\Result\\Reason\\UnusualElements' => $vendorDir . '/egulias/email-validator/src/Result/Reason/UnusualElements.php',
'Egulias\\EmailValidator\\Result\\Result' => $vendorDir . '/egulias/email-validator/src/Result/Result.php',
'Egulias\\EmailValidator\\Result\\SpoofEmail' => $vendorDir . '/egulias/email-validator/src/Result/SpoofEmail.php',
'Egulias\\EmailValidator\\Result\\ValidEmail' => $vendorDir . '/egulias/email-validator/src/Result/ValidEmail.php',
'Egulias\\EmailValidator\\Validation\\DNSCheckValidation' => $vendorDir . '/egulias/email-validator/src/Validation/DNSCheckValidation.php',
'Egulias\\EmailValidator\\Validation\\DNSGetRecordWrapper' => $vendorDir . '/egulias/email-validator/src/Validation/DNSGetRecordWrapper.php',
'Egulias\\EmailValidator\\Validation\\DNSRecords' => $vendorDir . '/egulias/email-validator/src/Validation/DNSRecords.php',
'Egulias\\EmailValidator\\Validation\\EmailValidation' => $vendorDir . '/egulias/email-validator/src/Validation/EmailValidation.php',
'Egulias\\EmailValidator\\Validation\\Exception\\EmptyValidationList' => $vendorDir . '/egulias/email-validator/src/Validation/Exception/EmptyValidationList.php',
'Egulias\\EmailValidator\\Validation\\Extra\\SpoofCheckValidation' => $vendorDir . '/egulias/email-validator/src/Validation/Extra/SpoofCheckValidation.php',
'Egulias\\EmailValidator\\Validation\\MessageIDValidation' => $vendorDir . '/egulias/email-validator/src/Validation/MessageIDValidation.php',
'Egulias\\EmailValidator\\Validation\\MultipleValidationWithAnd' => $vendorDir . '/egulias/email-validator/src/Validation/MultipleValidationWithAnd.php',
'Egulias\\EmailValidator\\Validation\\NoRFCWarningsValidation' => $vendorDir . '/egulias/email-validator/src/Validation/NoRFCWarningsValidation.php',
'Egulias\\EmailValidator\\Validation\\RFCValidation' => $vendorDir . '/egulias/email-validator/src/Validation/RFCValidation.php',
'Egulias\\EmailValidator\\Warning\\AddressLiteral' => $vendorDir . '/egulias/email-validator/src/Warning/AddressLiteral.php',
'Egulias\\EmailValidator\\Warning\\CFWSNearAt' => $vendorDir . '/egulias/email-validator/src/Warning/CFWSNearAt.php',
'Egulias\\EmailValidator\\Warning\\CFWSWithFWS' => $vendorDir . '/egulias/email-validator/src/Warning/CFWSWithFWS.php',
'Egulias\\EmailValidator\\Warning\\Comment' => $vendorDir . '/egulias/email-validator/src/Warning/Comment.php',
'Egulias\\EmailValidator\\Warning\\DeprecatedComment' => $vendorDir . '/egulias/email-validator/src/Warning/DeprecatedComment.php',
'Egulias\\EmailValidator\\Warning\\DomainLiteral' => $vendorDir . '/egulias/email-validator/src/Warning/DomainLiteral.php',
'Egulias\\EmailValidator\\Warning\\EmailTooLong' => $vendorDir . '/egulias/email-validator/src/Warning/EmailTooLong.php',
'Egulias\\EmailValidator\\Warning\\IPV6BadChar' => $vendorDir . '/egulias/email-validator/src/Warning/IPV6BadChar.php',
'Egulias\\EmailValidator\\Warning\\IPV6ColonEnd' => $vendorDir . '/egulias/email-validator/src/Warning/IPV6ColonEnd.php',
'Egulias\\EmailValidator\\Warning\\IPV6ColonStart' => $vendorDir . '/egulias/email-validator/src/Warning/IPV6ColonStart.php',
'Egulias\\EmailValidator\\Warning\\IPV6Deprecated' => $vendorDir . '/egulias/email-validator/src/Warning/IPV6Deprecated.php',
'Egulias\\EmailValidator\\Warning\\IPV6DoubleColon' => $vendorDir . '/egulias/email-validator/src/Warning/IPV6DoubleColon.php',
'Egulias\\EmailValidator\\Warning\\IPV6GroupCount' => $vendorDir . '/egulias/email-validator/src/Warning/IPV6GroupCount.php',
'Egulias\\EmailValidator\\Warning\\IPV6MaxGroups' => $vendorDir . '/egulias/email-validator/src/Warning/IPV6MaxGroups.php',
'Egulias\\EmailValidator\\Warning\\LocalTooLong' => $vendorDir . '/egulias/email-validator/src/Warning/LocalTooLong.php',
'Egulias\\EmailValidator\\Warning\\NoDNSMXRecord' => $vendorDir . '/egulias/email-validator/src/Warning/NoDNSMXRecord.php',
'Egulias\\EmailValidator\\Warning\\ObsoleteDTEXT' => $vendorDir . '/egulias/email-validator/src/Warning/ObsoleteDTEXT.php',
'Egulias\\EmailValidator\\Warning\\QuotedPart' => $vendorDir . '/egulias/email-validator/src/Warning/QuotedPart.php',
'Egulias\\EmailValidator\\Warning\\QuotedString' => $vendorDir . '/egulias/email-validator/src/Warning/QuotedString.php',
'Egulias\\EmailValidator\\Warning\\TLD' => $vendorDir . '/egulias/email-validator/src/Warning/TLD.php',
'Egulias\\EmailValidator\\Warning\\Warning' => $vendorDir . '/egulias/email-validator/src/Warning/Warning.php',
'Filter\\FilterException' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterException.php',
'Filter\\FilterFailedException' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/Filter/FilterFailedException.php',
'GuzzleHttp\\Psr7\\AppendStream' => $vendorDir . '/guzzlehttp/psr7/src/AppendStream.php',
'GuzzleHttp\\Psr7\\BufferStream' => $vendorDir . '/guzzlehttp/psr7/src/BufferStream.php',
'GuzzleHttp\\Psr7\\CachingStream' => $vendorDir . '/guzzlehttp/psr7/src/CachingStream.php',
'GuzzleHttp\\Psr7\\DroppingStream' => $vendorDir . '/guzzlehttp/psr7/src/DroppingStream.php',
'GuzzleHttp\\Psr7\\Exception\\MalformedUriException' => $vendorDir . '/guzzlehttp/psr7/src/Exception/MalformedUriException.php',
'GuzzleHttp\\Psr7\\FnStream' => $vendorDir . '/guzzlehttp/psr7/src/FnStream.php',
'GuzzleHttp\\Psr7\\Header' => $vendorDir . '/guzzlehttp/psr7/src/Header.php',
'GuzzleHttp\\Psr7\\HttpFactory' => $vendorDir . '/guzzlehttp/psr7/src/HttpFactory.php',
'GuzzleHttp\\Psr7\\InflateStream' => $vendorDir . '/guzzlehttp/psr7/src/InflateStream.php',
'GuzzleHttp\\Psr7\\LazyOpenStream' => $vendorDir . '/guzzlehttp/psr7/src/LazyOpenStream.php',
'GuzzleHttp\\Psr7\\LimitStream' => $vendorDir . '/guzzlehttp/psr7/src/LimitStream.php',
'GuzzleHttp\\Psr7\\Message' => $vendorDir . '/guzzlehttp/psr7/src/Message.php',
'GuzzleHttp\\Psr7\\MessageTrait' => $vendorDir . '/guzzlehttp/psr7/src/MessageTrait.php',
'GuzzleHttp\\Psr7\\MimeType' => $vendorDir . '/guzzlehttp/psr7/src/MimeType.php',
'GuzzleHttp\\Psr7\\MultipartStream' => $vendorDir . '/guzzlehttp/psr7/src/MultipartStream.php',
'GuzzleHttp\\Psr7\\NoSeekStream' => $vendorDir . '/guzzlehttp/psr7/src/NoSeekStream.php',
'GuzzleHttp\\Psr7\\PumpStream' => $vendorDir . '/guzzlehttp/psr7/src/PumpStream.php',
'GuzzleHttp\\Psr7\\Query' => $vendorDir . '/guzzlehttp/psr7/src/Query.php',
'GuzzleHttp\\Psr7\\Request' => $vendorDir . '/guzzlehttp/psr7/src/Request.php',
'GuzzleHttp\\Psr7\\Response' => $vendorDir . '/guzzlehttp/psr7/src/Response.php',
'GuzzleHttp\\Psr7\\Rfc3986' => $vendorDir . '/guzzlehttp/psr7/src/Rfc3986.php',
'GuzzleHttp\\Psr7\\Rfc7230' => $vendorDir . '/guzzlehttp/psr7/src/Rfc7230.php',
'GuzzleHttp\\Psr7\\ServerRequest' => $vendorDir . '/guzzlehttp/psr7/src/ServerRequest.php',
'GuzzleHttp\\Psr7\\Stream' => $vendorDir . '/guzzlehttp/psr7/src/Stream.php',
'GuzzleHttp\\Psr7\\StreamDecoratorTrait' => $vendorDir . '/guzzlehttp/psr7/src/StreamDecoratorTrait.php',
'GuzzleHttp\\Psr7\\StreamWrapper' => $vendorDir . '/guzzlehttp/psr7/src/StreamWrapper.php',
'GuzzleHttp\\Psr7\\UploadedFile' => $vendorDir . '/guzzlehttp/psr7/src/UploadedFile.php',
'GuzzleHttp\\Psr7\\Uri' => $vendorDir . '/guzzlehttp/psr7/src/Uri.php',
'GuzzleHttp\\Psr7\\UriComparator' => $vendorDir . '/guzzlehttp/psr7/src/UriComparator.php',
'GuzzleHttp\\Psr7\\UriNormalizer' => $vendorDir . '/guzzlehttp/psr7/src/UriNormalizer.php',
'GuzzleHttp\\Psr7\\UriResolver' => $vendorDir . '/guzzlehttp/psr7/src/UriResolver.php',
'GuzzleHttp\\Psr7\\Utils' => $vendorDir . '/guzzlehttp/psr7/src/Utils.php',
'Illuminate\\Contracts\\Auth\\Access\\Authorizable' => $vendorDir . '/illuminate/contracts/Auth/Access/Authorizable.php',
'Illuminate\\Contracts\\Auth\\Access\\Gate' => $vendorDir . '/illuminate/contracts/Auth/Access/Gate.php',
'Illuminate\\Contracts\\Auth\\Authenticatable' => $vendorDir . '/illuminate/contracts/Auth/Authenticatable.php',
'Illuminate\\Contracts\\Auth\\CanResetPassword' => $vendorDir . '/illuminate/contracts/Auth/CanResetPassword.php',
'Illuminate\\Contracts\\Auth\\Factory' => $vendorDir . '/illuminate/contracts/Auth/Factory.php',
'Illuminate\\Contracts\\Auth\\Guard' => $vendorDir . '/illuminate/contracts/Auth/Guard.php',
'Illuminate\\Contracts\\Auth\\Middleware\\AuthenticatesRequests' => $vendorDir . '/illuminate/contracts/Auth/Middleware/AuthenticatesRequests.php',
'Illuminate\\Contracts\\Auth\\MustVerifyEmail' => $vendorDir . '/illuminate/contracts/Auth/MustVerifyEmail.php',
'Illuminate\\Contracts\\Auth\\PasswordBroker' => $vendorDir . '/illuminate/contracts/Auth/PasswordBroker.php',
'Illuminate\\Contracts\\Auth\\PasswordBrokerFactory' => $vendorDir . '/illuminate/contracts/Auth/PasswordBrokerFactory.php',
'Illuminate\\Contracts\\Auth\\StatefulGuard' => $vendorDir . '/illuminate/contracts/Auth/StatefulGuard.php',
'Illuminate\\Contracts\\Auth\\SupportsBasicAuth' => $vendorDir . '/illuminate/contracts/Auth/SupportsBasicAuth.php',
'Illuminate\\Contracts\\Auth\\UserProvider' => $vendorDir . '/illuminate/contracts/Auth/UserProvider.php',
'Illuminate\\Contracts\\Broadcasting\\Broadcaster' => $vendorDir . '/illuminate/contracts/Broadcasting/Broadcaster.php',
'Illuminate\\Contracts\\Broadcasting\\Factory' => $vendorDir . '/illuminate/contracts/Broadcasting/Factory.php',
'Illuminate\\Contracts\\Broadcasting\\HasBroadcastChannel' => $vendorDir . '/illuminate/contracts/Broadcasting/HasBroadcastChannel.php',
'Illuminate\\Contracts\\Broadcasting\\ShouldBeUnique' => $vendorDir . '/illuminate/contracts/Broadcasting/ShouldBeUnique.php',
'Illuminate\\Contracts\\Broadcasting\\ShouldBroadcast' => $vendorDir . '/illuminate/contracts/Broadcasting/ShouldBroadcast.php',
'Illuminate\\Contracts\\Broadcasting\\ShouldBroadcastNow' => $vendorDir . '/illuminate/contracts/Broadcasting/ShouldBroadcastNow.php',
'Illuminate\\Contracts\\Broadcasting\\ShouldRescue' => $vendorDir . '/illuminate/contracts/Broadcasting/ShouldRescue.php',
'Illuminate\\Contracts\\Bus\\Dispatcher' => $vendorDir . '/illuminate/contracts/Bus/Dispatcher.php',
'Illuminate\\Contracts\\Bus\\QueueingDispatcher' => $vendorDir . '/illuminate/contracts/Bus/QueueingDispatcher.php',
'Illuminate\\Contracts\\Cache\\Factory' => $vendorDir . '/illuminate/contracts/Cache/Factory.php',
'Illuminate\\Contracts\\Cache\\Lock' => $vendorDir . '/illuminate/contracts/Cache/Lock.php',
'Illuminate\\Contracts\\Cache\\LockProvider' => $vendorDir . '/illuminate/contracts/Cache/LockProvider.php',
'Illuminate\\Contracts\\Cache\\LockTimeoutException' => $vendorDir . '/illuminate/contracts/Cache/LockTimeoutException.php',
'Illuminate\\Contracts\\Cache\\Repository' => $vendorDir . '/illuminate/contracts/Cache/Repository.php',
'Illuminate\\Contracts\\Cache\\Store' => $vendorDir . '/illuminate/contracts/Cache/Store.php',
'Illuminate\\Contracts\\Concurrency\\Driver' => $vendorDir . '/illuminate/contracts/Concurrency/Driver.php',
'Illuminate\\Contracts\\Config\\Repository' => $vendorDir . '/illuminate/contracts/Config/Repository.php',
'Illuminate\\Contracts\\Console\\Application' => $vendorDir . '/illuminate/contracts/Console/Application.php',
'Illuminate\\Contracts\\Console\\Isolatable' => $vendorDir . '/illuminate/contracts/Console/Isolatable.php',
'Illuminate\\Contracts\\Console\\Kernel' => $vendorDir . '/illuminate/contracts/Console/Kernel.php',
'Illuminate\\Contracts\\Console\\PromptsForMissingInput' => $vendorDir . '/illuminate/contracts/Console/PromptsForMissingInput.php',
'Illuminate\\Contracts\\Container\\BindingResolutionException' => $vendorDir . '/illuminate/contracts/Container/BindingResolutionException.php',
'Illuminate\\Contracts\\Container\\CircularDependencyException' => $vendorDir . '/illuminate/contracts/Container/CircularDependencyException.php',
'Illuminate\\Contracts\\Container\\Container' => $vendorDir . '/illuminate/contracts/Container/Container.php',
'Illuminate\\Contracts\\Container\\ContextualAttribute' => $vendorDir . '/illuminate/contracts/Container/ContextualAttribute.php',
'Illuminate\\Contracts\\Container\\ContextualBindingBuilder' => $vendorDir . '/illuminate/contracts/Container/ContextualBindingBuilder.php',
'Illuminate\\Contracts\\Container\\SelfBuilding' => $vendorDir . '/illuminate/contracts/Container/SelfBuilding.php',
'Illuminate\\Contracts\\Cookie\\Factory' => $vendorDir . '/illuminate/contracts/Cookie/Factory.php',
'Illuminate\\Contracts\\Cookie\\QueueingFactory' => $vendorDir . '/illuminate/contracts/Cookie/QueueingFactory.php',
'Illuminate\\Contracts\\Database\\ConcurrencyErrorDetector' => $vendorDir . '/illuminate/contracts/Database/ConcurrencyErrorDetector.php',
'Illuminate\\Contracts\\Database\\Eloquent\\Builder' => $vendorDir . '/illuminate/contracts/Database/Eloquent/Builder.php',
'Illuminate\\Contracts\\Database\\Eloquent\\Castable' => $vendorDir . '/illuminate/contracts/Database/Eloquent/Castable.php',
'Illuminate\\Contracts\\Database\\Eloquent\\CastsAttributes' => $vendorDir . '/illuminate/contracts/Database/Eloquent/CastsAttributes.php',
'Illuminate\\Contracts\\Database\\Eloquent\\CastsInboundAttributes' => $vendorDir . '/illuminate/contracts/Database/Eloquent/CastsInboundAttributes.php',
'Illuminate\\Contracts\\Database\\Eloquent\\ComparesCastableAttributes' => $vendorDir . '/illuminate/contracts/Database/Eloquent/ComparesCastableAttributes.php',
'Illuminate\\Contracts\\Database\\Eloquent\\DeviatesCastableAttributes' => $vendorDir . '/illuminate/contracts/Database/Eloquent/DeviatesCastableAttributes.php',
'Illuminate\\Contracts\\Database\\Eloquent\\SerializesCastableAttributes' => $vendorDir . '/illuminate/contracts/Database/Eloquent/SerializesCastableAttributes.php',
'Illuminate\\Contracts\\Database\\Eloquent\\SupportsPartialRelations' => $vendorDir . '/illuminate/contracts/Database/Eloquent/SupportsPartialRelations.php',
'Illuminate\\Contracts\\Database\\Events\\MigrationEvent' => $vendorDir . '/illuminate/contracts/Database/Events/MigrationEvent.php',
'Illuminate\\Contracts\\Database\\LostConnectionDetector' => $vendorDir . '/illuminate/contracts/Database/LostConnectionDetector.php',
'Illuminate\\Contracts\\Database\\ModelIdentifier' => $vendorDir . '/illuminate/contracts/Database/ModelIdentifier.php',
'Illuminate\\Contracts\\Database\\Query\\Builder' => $vendorDir . '/illuminate/contracts/Database/Query/Builder.php',
'Illuminate\\Contracts\\Database\\Query\\ConditionExpression' => $vendorDir . '/illuminate/contracts/Database/Query/ConditionExpression.php',
'Illuminate\\Contracts\\Database\\Query\\Expression' => $vendorDir . '/illuminate/contracts/Database/Query/Expression.php',
'Illuminate\\Contracts\\Debug\\ExceptionHandler' => $vendorDir . '/illuminate/contracts/Debug/ExceptionHandler.php',
'Illuminate\\Contracts\\Debug\\ShouldntReport' => $vendorDir . '/illuminate/contracts/Debug/ShouldntReport.php',
'Illuminate\\Contracts\\Encryption\\DecryptException' => $vendorDir . '/illuminate/contracts/Encryption/DecryptException.php',
'Illuminate\\Contracts\\Encryption\\EncryptException' => $vendorDir . '/illuminate/contracts/Encryption/EncryptException.php',
'Illuminate\\Contracts\\Encryption\\Encrypter' => $vendorDir . '/illuminate/contracts/Encryption/Encrypter.php',
'Illuminate\\Contracts\\Encryption\\StringEncrypter' => $vendorDir . '/illuminate/contracts/Encryption/StringEncrypter.php',
'Illuminate\\Contracts\\Events\\Dispatcher' => $vendorDir . '/illuminate/contracts/Events/Dispatcher.php',
'Illuminate\\Contracts\\Events\\ShouldDispatchAfterCommit' => $vendorDir . '/illuminate/contracts/Events/ShouldDispatchAfterCommit.php',
'Illuminate\\Contracts\\Events\\ShouldHandleEventsAfterCommit' => $vendorDir . '/illuminate/contracts/Events/ShouldHandleEventsAfterCommit.php',
'Illuminate\\Contracts\\Filesystem\\Cloud' => $vendorDir . '/illuminate/contracts/Filesystem/Cloud.php',
'Illuminate\\Contracts\\Filesystem\\Factory' => $vendorDir . '/illuminate/contracts/Filesystem/Factory.php',
'Illuminate\\Contracts\\Filesystem\\FileNotFoundException' => $vendorDir . '/illuminate/contracts/Filesystem/FileNotFoundException.php',
'Illuminate\\Contracts\\Filesystem\\Filesystem' => $vendorDir . '/illuminate/contracts/Filesystem/Filesystem.php',
'Illuminate\\Contracts\\Filesystem\\LockTimeoutException' => $vendorDir . '/illuminate/contracts/Filesystem/LockTimeoutException.php',
'Illuminate\\Contracts\\Foundation\\Application' => $vendorDir . '/illuminate/contracts/Foundation/Application.php',
'Illuminate\\Contracts\\Foundation\\CachesConfiguration' => $vendorDir . '/illuminate/contracts/Foundation/CachesConfiguration.php',
'Illuminate\\Contracts\\Foundation\\CachesRoutes' => $vendorDir . '/illuminate/contracts/Foundation/CachesRoutes.php',
'Illuminate\\Contracts\\Foundation\\ExceptionRenderer' => $vendorDir . '/illuminate/contracts/Foundation/ExceptionRenderer.php',
'Illuminate\\Contracts\\Foundation\\MaintenanceMode' => $vendorDir . '/illuminate/contracts/Foundation/MaintenanceMode.php',
'Illuminate\\Contracts\\Hashing\\Hasher' => $vendorDir . '/illuminate/contracts/Hashing/Hasher.php',
'Illuminate\\Contracts\\Http\\Kernel' => $vendorDir . '/illuminate/contracts/Http/Kernel.php',
'Illuminate\\Contracts\\JsonSchema\\JsonSchema' => $vendorDir . '/illuminate/contracts/JsonSchema/JsonSchema.php',
'Illuminate\\Contracts\\Log\\ContextLogProcessor' => $vendorDir . '/illuminate/contracts/Log/ContextLogProcessor.php',
'Illuminate\\Contracts\\Mail\\Attachable' => $vendorDir . '/illuminate/contracts/Mail/Attachable.php',
'Illuminate\\Contracts\\Mail\\Factory' => $vendorDir . '/illuminate/contracts/Mail/Factory.php',
'Illuminate\\Contracts\\Mail\\MailQueue' => $vendorDir . '/illuminate/contracts/Mail/MailQueue.php',
'Illuminate\\Contracts\\Mail\\Mailable' => $vendorDir . '/illuminate/contracts/Mail/Mailable.php',
'Illuminate\\Contracts\\Mail\\Mailer' => $vendorDir . '/illuminate/contracts/Mail/Mailer.php',
'Illuminate\\Contracts\\Notifications\\Dispatcher' => $vendorDir . '/illuminate/contracts/Notifications/Dispatcher.php',
'Illuminate\\Contracts\\Notifications\\Factory' => $vendorDir . '/illuminate/contracts/Notifications/Factory.php',
'Illuminate\\Contracts\\Pagination\\CursorPaginator' => $vendorDir . '/illuminate/contracts/Pagination/CursorPaginator.php',
'Illuminate\\Contracts\\Pagination\\LengthAwarePaginator' => $vendorDir . '/illuminate/contracts/Pagination/LengthAwarePaginator.php',
'Illuminate\\Contracts\\Pagination\\Paginator' => $vendorDir . '/illuminate/contracts/Pagination/Paginator.php',
'Illuminate\\Contracts\\Pipeline\\Hub' => $vendorDir . '/illuminate/contracts/Pipeline/Hub.php',
'Illuminate\\Contracts\\Pipeline\\Pipeline' => $vendorDir . '/illuminate/contracts/Pipeline/Pipeline.php',
'Illuminate\\Contracts\\Process\\InvokedProcess' => $vendorDir . '/illuminate/contracts/Process/InvokedProcess.php',
'Illuminate\\Contracts\\Process\\ProcessResult' => $vendorDir . '/illuminate/contracts/Process/ProcessResult.php',
'Illuminate\\Contracts\\Queue\\ClearableQueue' => $vendorDir . '/illuminate/contracts/Queue/ClearableQueue.php',
'Illuminate\\Contracts\\Queue\\EntityNotFoundException' => $vendorDir . '/illuminate/contracts/Queue/EntityNotFoundException.php',
'Illuminate\\Contracts\\Queue\\EntityResolver' => $vendorDir . '/illuminate/contracts/Queue/EntityResolver.php',
'Illuminate\\Contracts\\Queue\\Factory' => $vendorDir . '/illuminate/contracts/Queue/Factory.php',
'Illuminate\\Contracts\\Queue\\Job' => $vendorDir . '/illuminate/contracts/Queue/Job.php',
'Illuminate\\Contracts\\Queue\\Monitor' => $vendorDir . '/illuminate/contracts/Queue/Monitor.php',
'Illuminate\\Contracts\\Queue\\Queue' => $vendorDir . '/illuminate/contracts/Queue/Queue.php',
'Illuminate\\Contracts\\Queue\\QueueableCollection' => $vendorDir . '/illuminate/contracts/Queue/QueueableCollection.php',
'Illuminate\\Contracts\\Queue\\QueueableEntity' => $vendorDir . '/illuminate/contracts/Queue/QueueableEntity.php',
'Illuminate\\Contracts\\Queue\\ShouldBeEncrypted' => $vendorDir . '/illuminate/contracts/Queue/ShouldBeEncrypted.php',
'Illuminate\\Contracts\\Queue\\ShouldBeUnique' => $vendorDir . '/illuminate/contracts/Queue/ShouldBeUnique.php',
'Illuminate\\Contracts\\Queue\\ShouldBeUniqueUntilProcessing' => $vendorDir . '/illuminate/contracts/Queue/ShouldBeUniqueUntilProcessing.php',
'Illuminate\\Contracts\\Queue\\ShouldQueue' => $vendorDir . '/illuminate/contracts/Queue/ShouldQueue.php',
'Illuminate\\Contracts\\Queue\\ShouldQueueAfterCommit' => $vendorDir . '/illuminate/contracts/Queue/ShouldQueueAfterCommit.php',
'Illuminate\\Contracts\\Redis\\Connection' => $vendorDir . '/illuminate/contracts/Redis/Connection.php',
'Illuminate\\Contracts\\Redis\\Connector' => $vendorDir . '/illuminate/contracts/Redis/Connector.php',
'Illuminate\\Contracts\\Redis\\Factory' => $vendorDir . '/illuminate/contracts/Redis/Factory.php',
'Illuminate\\Contracts\\Redis\\LimiterTimeoutException' => $vendorDir . '/illuminate/contracts/Redis/LimiterTimeoutException.php',
'Illuminate\\Contracts\\Routing\\BindingRegistrar' => $vendorDir . '/illuminate/contracts/Routing/BindingRegistrar.php',
'Illuminate\\Contracts\\Routing\\Registrar' => $vendorDir . '/illuminate/contracts/Routing/Registrar.php',
'Illuminate\\Contracts\\Routing\\ResponseFactory' => $vendorDir . '/illuminate/contracts/Routing/ResponseFactory.php',
'Illuminate\\Contracts\\Routing\\UrlGenerator' => $vendorDir . '/illuminate/contracts/Routing/UrlGenerator.php',
'Illuminate\\Contracts\\Routing\\UrlRoutable' => $vendorDir . '/illuminate/contracts/Routing/UrlRoutable.php',
'Illuminate\\Contracts\\Session\\Middleware\\AuthenticatesSessions' => $vendorDir . '/illuminate/contracts/Session/Middleware/AuthenticatesSessions.php',
'Illuminate\\Contracts\\Session\\Session' => $vendorDir . '/illuminate/contracts/Session/Session.php',
'Illuminate\\Contracts\\Support\\Arrayable' => $vendorDir . '/illuminate/contracts/Support/Arrayable.php',
'Illuminate\\Contracts\\Support\\CanBeEscapedWhenCastToString' => $vendorDir . '/illuminate/contracts/Support/CanBeEscapedWhenCastToString.php',
'Illuminate\\Contracts\\Support\\DeferrableProvider' => $vendorDir . '/illuminate/contracts/Support/DeferrableProvider.php',
'Illuminate\\Contracts\\Support\\DeferringDisplayableValue' => $vendorDir . '/illuminate/contracts/Support/DeferringDisplayableValue.php',
'Illuminate\\Contracts\\Support\\HasOnceHash' => $vendorDir . '/illuminate/contracts/Support/HasOnceHash.php',
'Illuminate\\Contracts\\Support\\Htmlable' => $vendorDir . '/illuminate/contracts/Support/Htmlable.php',
'Illuminate\\Contracts\\Support\\Jsonable' => $vendorDir . '/illuminate/contracts/Support/Jsonable.php',
'Illuminate\\Contracts\\Support\\MessageBag' => $vendorDir . '/illuminate/contracts/Support/MessageBag.php',
'Illuminate\\Contracts\\Support\\MessageProvider' => $vendorDir . '/illuminate/contracts/Support/MessageProvider.php',
'Illuminate\\Contracts\\Support\\Renderable' => $vendorDir . '/illuminate/contracts/Support/Renderable.php',
'Illuminate\\Contracts\\Support\\Responsable' => $vendorDir . '/illuminate/contracts/Support/Responsable.php',
'Illuminate\\Contracts\\Support\\ValidatedData' => $vendorDir . '/illuminate/contracts/Support/ValidatedData.php',
'Illuminate\\Contracts\\Translation\\HasLocalePreference' => $vendorDir . '/illuminate/contracts/Translation/HasLocalePreference.php',
'Illuminate\\Contracts\\Translation\\Loader' => $vendorDir . '/illuminate/contracts/Translation/Loader.php',
'Illuminate\\Contracts\\Translation\\Translator' => $vendorDir . '/illuminate/contracts/Translation/Translator.php',
'Illuminate\\Contracts\\Validation\\CompilableRules' => $vendorDir . '/illuminate/contracts/Validation/CompilableRules.php',
'Illuminate\\Contracts\\Validation\\DataAwareRule' => $vendorDir . '/illuminate/contracts/Validation/DataAwareRule.php',
'Illuminate\\Contracts\\Validation\\Factory' => $vendorDir . '/illuminate/contracts/Validation/Factory.php',
'Illuminate\\Contracts\\Validation\\ImplicitRule' => $vendorDir . '/illuminate/contracts/Validation/ImplicitRule.php',
'Illuminate\\Contracts\\Validation\\InvokableRule' => $vendorDir . '/illuminate/contracts/Validation/InvokableRule.php',
'Illuminate\\Contracts\\Validation\\Rule' => $vendorDir . '/illuminate/contracts/Validation/Rule.php',
'Illuminate\\Contracts\\Validation\\UncompromisedVerifier' => $vendorDir . '/illuminate/contracts/Validation/UncompromisedVerifier.php',
'Illuminate\\Contracts\\Validation\\ValidatesWhenResolved' => $vendorDir . '/illuminate/contracts/Validation/ValidatesWhenResolved.php',
'Illuminate\\Contracts\\Validation\\ValidationRule' => $vendorDir . '/illuminate/contracts/Validation/ValidationRule.php',
'Illuminate\\Contracts\\Validation\\Validator' => $vendorDir . '/illuminate/contracts/Validation/Validator.php',
'Illuminate\\Contracts\\Validation\\ValidatorAwareRule' => $vendorDir . '/illuminate/contracts/Validation/ValidatorAwareRule.php',
'Illuminate\\Contracts\\View\\Engine' => $vendorDir . '/illuminate/contracts/View/Engine.php',
'Illuminate\\Contracts\\View\\Factory' => $vendorDir . '/illuminate/contracts/View/Factory.php',
'Illuminate\\Contracts\\View\\View' => $vendorDir . '/illuminate/contracts/View/View.php',
'Illuminate\\Contracts\\View\\ViewCompilationException' => $vendorDir . '/illuminate/contracts/View/ViewCompilationException.php',
'Illuminate\\Support\\Arr' => $vendorDir . '/illuminate/collections/Arr.php',
'Illuminate\\Support\\Collection' => $vendorDir . '/illuminate/collections/Collection.php',
'Illuminate\\Support\\Enumerable' => $vendorDir . '/illuminate/collections/Enumerable.php',
'Illuminate\\Support\\HigherOrderCollectionProxy' => $vendorDir . '/illuminate/collections/HigherOrderCollectionProxy.php',
'Illuminate\\Support\\HigherOrderWhenProxy' => $vendorDir . '/illuminate/conditionable/HigherOrderWhenProxy.php',
'Illuminate\\Support\\ItemNotFoundException' => $vendorDir . '/illuminate/collections/ItemNotFoundException.php',
'Illuminate\\Support\\LazyCollection' => $vendorDir . '/illuminate/collections/LazyCollection.php',
'Illuminate\\Support\\MultipleItemsFoundException' => $vendorDir . '/illuminate/collections/MultipleItemsFoundException.php',
'Illuminate\\Support\\Traits\\Conditionable' => $vendorDir . '/illuminate/conditionable/Traits/Conditionable.php',
'Illuminate\\Support\\Traits\\EnumeratesValues' => $vendorDir . '/illuminate/collections/Traits/EnumeratesValues.php',
'Illuminate\\Support\\Traits\\Macroable' => $vendorDir . '/illuminate/macroable/Traits/Macroable.php',
'Illuminate\\Support\\Traits\\TransformsToResourceCollection' => $vendorDir . '/illuminate/collections/Traits/TransformsToResourceCollection.php',
'Invoker\\CallableResolver' => $vendorDir . '/php-di/invoker/src/CallableResolver.php',
'Invoker\\Exception\\InvocationException' => $vendorDir . '/php-di/invoker/src/Exception/InvocationException.php',
'Invoker\\Exception\\NotCallableException' => $vendorDir . '/php-di/invoker/src/Exception/NotCallableException.php',
'Invoker\\Exception\\NotEnoughParametersException' => $vendorDir . '/php-di/invoker/src/Exception/NotEnoughParametersException.php',
'Invoker\\Invoker' => $vendorDir . '/php-di/invoker/src/Invoker.php',
'Invoker\\InvokerInterface' => $vendorDir . '/php-di/invoker/src/InvokerInterface.php',
'Invoker\\ParameterResolver\\AssociativeArrayResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/AssociativeArrayResolver.php',
'Invoker\\ParameterResolver\\Container\\ParameterNameContainerResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/Container/ParameterNameContainerResolver.php',
'Invoker\\ParameterResolver\\Container\\TypeHintContainerResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/Container/TypeHintContainerResolver.php',
'Invoker\\ParameterResolver\\DefaultValueResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/DefaultValueResolver.php',
'Invoker\\ParameterResolver\\NumericArrayResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/NumericArrayResolver.php',
'Invoker\\ParameterResolver\\ParameterResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/ParameterResolver.php',
'Invoker\\ParameterResolver\\ResolverChain' => $vendorDir . '/php-di/invoker/src/ParameterResolver/ResolverChain.php',
'Invoker\\ParameterResolver\\TypeHintResolver' => $vendorDir . '/php-di/invoker/src/ParameterResolver/TypeHintResolver.php',
'Invoker\\Reflection\\CallableReflection' => $vendorDir . '/php-di/invoker/src/Reflection/CallableReflection.php',
'Laravel\\SerializableClosure\\Contracts\\Serializable' => $vendorDir . '/laravel/serializable-closure/src/Contracts/Serializable.php',
'Laravel\\SerializableClosure\\Contracts\\Signer' => $vendorDir . '/laravel/serializable-closure/src/Contracts/Signer.php',
'Laravel\\SerializableClosure\\Exceptions\\InvalidSignatureException' => $vendorDir . '/laravel/serializable-closure/src/Exceptions/InvalidSignatureException.php',
'Laravel\\SerializableClosure\\Exceptions\\MissingSecretKeyException' => $vendorDir . '/laravel/serializable-closure/src/Exceptions/MissingSecretKeyException.php',
'Laravel\\SerializableClosure\\SerializableClosure' => $vendorDir . '/laravel/serializable-closure/src/SerializableClosure.php',
'Laravel\\SerializableClosure\\Serializers\\Native' => $vendorDir . '/laravel/serializable-closure/src/Serializers/Native.php',
'Laravel\\SerializableClosure\\Serializers\\Signed' => $vendorDir . '/laravel/serializable-closure/src/Serializers/Signed.php',
'Laravel\\SerializableClosure\\Signers\\Hmac' => $vendorDir . '/laravel/serializable-closure/src/Signers/Hmac.php',
'Laravel\\SerializableClosure\\Support\\ClosureScope' => $vendorDir . '/laravel/serializable-closure/src/Support/ClosureScope.php',
'Laravel\\SerializableClosure\\Support\\ClosureStream' => $vendorDir . '/laravel/serializable-closure/src/Support/ClosureStream.php',
'Laravel\\SerializableClosure\\Support\\ReflectionClosure' => $vendorDir . '/laravel/serializable-closure/src/Support/ReflectionClosure.php',
'Laravel\\SerializableClosure\\Support\\SelfReference' => $vendorDir . '/laravel/serializable-closure/src/Support/SelfReference.php',
'Laravel\\SerializableClosure\\UnsignedSerializableClosure' => $vendorDir . '/laravel/serializable-closure/src/UnsignedSerializableClosure.php',
'NoDiscard' => $vendorDir . '/symfony/polyfill-php85/Resources/stubs/NoDiscard.php',
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
'Override' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/Override.php',
'Pdo\\Dblib' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Dblib.php',
'Pdo\\Firebird' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Firebird.php',
'Pdo\\Mysql' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Mysql.php',
'Pdo\\Odbc' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Odbc.php',
'Pdo\\Pgsql' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Pgsql.php',
'Pdo\\Sqlite' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/Pdo/Sqlite.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Psr\\Clock\\ClockInterface' => $vendorDir . '/psr/clock/src/ClockInterface.php',
'Psr\\Container\\ContainerExceptionInterface' => $vendorDir . '/psr/container/src/ContainerExceptionInterface.php',
'Psr\\Container\\ContainerInterface' => $vendorDir . '/psr/container/src/ContainerInterface.php',
'Psr\\Container\\NotFoundExceptionInterface' => $vendorDir . '/psr/container/src/NotFoundExceptionInterface.php',
'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php',
'Psr\\Http\\Message\\RequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/RequestFactoryInterface.php',
'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php',
'Psr\\Http\\Message\\ResponseFactoryInterface' => $vendorDir . '/psr/http-factory/src/ResponseFactoryInterface.php',
'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php',
'Psr\\Http\\Message\\ServerRequestFactoryInterface' => $vendorDir . '/psr/http-factory/src/ServerRequestFactoryInterface.php',
'Psr\\Http\\Message\\ServerRequestInterface' => $vendorDir . '/psr/http-message/src/ServerRequestInterface.php',
'Psr\\Http\\Message\\StreamFactoryInterface' => $vendorDir . '/psr/http-factory/src/StreamFactoryInterface.php',
'Psr\\Http\\Message\\StreamInterface' => $vendorDir . '/psr/http-message/src/StreamInterface.php',
'Psr\\Http\\Message\\UploadedFileFactoryInterface' => $vendorDir . '/psr/http-factory/src/UploadedFileFactoryInterface.php',
'Psr\\Http\\Message\\UploadedFileInterface' => $vendorDir . '/psr/http-message/src/UploadedFileInterface.php',
'Psr\\Http\\Message\\UriFactoryInterface' => $vendorDir . '/psr/http-factory/src/UriFactoryInterface.php',
'Psr\\Http\\Message\\UriInterface' => $vendorDir . '/psr/http-message/src/UriInterface.php',
'Psr\\Log\\AbstractLogger' => $vendorDir . '/psr/log/src/AbstractLogger.php',
'Psr\\Log\\InvalidArgumentException' => $vendorDir . '/psr/log/src/InvalidArgumentException.php',
'Psr\\Log\\LogLevel' => $vendorDir . '/psr/log/src/LogLevel.php',
'Psr\\Log\\LoggerAwareInterface' => $vendorDir . '/psr/log/src/LoggerAwareInterface.php',
'Psr\\Log\\LoggerAwareTrait' => $vendorDir . '/psr/log/src/LoggerAwareTrait.php',
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/src/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/src/LoggerTrait.php',
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/src/NullLogger.php',
'Psr\\SimpleCache\\CacheException' => $vendorDir . '/psr/simple-cache/src/CacheException.php',
'Psr\\SimpleCache\\CacheInterface' => $vendorDir . '/psr/simple-cache/src/CacheInterface.php',
'Psr\\SimpleCache\\InvalidArgumentException' => $vendorDir . '/psr/simple-cache/src/InvalidArgumentException.php',
'ReflectionConstant' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/ReflectionConstant.php',
'RoundingMode' => $vendorDir . '/symfony/polyfill-php84/Resources/stubs/RoundingMode.php',
'SQLite3Exception' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'Symfony\\Component\\Clock\\Clock' => $vendorDir . '/symfony/clock/Clock.php',
'Symfony\\Component\\Clock\\ClockAwareTrait' => $vendorDir . '/symfony/clock/ClockAwareTrait.php',
'Symfony\\Component\\Clock\\ClockInterface' => $vendorDir . '/symfony/clock/ClockInterface.php',
'Symfony\\Component\\Clock\\DatePoint' => $vendorDir . '/symfony/clock/DatePoint.php',
'Symfony\\Component\\Clock\\MockClock' => $vendorDir . '/symfony/clock/MockClock.php',
'Symfony\\Component\\Clock\\MonotonicClock' => $vendorDir . '/symfony/clock/MonotonicClock.php',
'Symfony\\Component\\Clock\\NativeClock' => $vendorDir . '/symfony/clock/NativeClock.php',
'Symfony\\Component\\Clock\\Test\\ClockSensitiveTrait' => $vendorDir . '/symfony/clock/Test/ClockSensitiveTrait.php',
'Symfony\\Component\\Mime\\Address' => $vendorDir . '/symfony/mime/Address.php',
'Symfony\\Component\\Mime\\BodyRendererInterface' => $vendorDir . '/symfony/mime/BodyRendererInterface.php',
'Symfony\\Component\\Mime\\CharacterStream' => $vendorDir . '/symfony/mime/CharacterStream.php',
'Symfony\\Component\\Mime\\Crypto\\DkimOptions' => $vendorDir . '/symfony/mime/Crypto/DkimOptions.php',
'Symfony\\Component\\Mime\\Crypto\\DkimSigner' => $vendorDir . '/symfony/mime/Crypto/DkimSigner.php',
'Symfony\\Component\\Mime\\Crypto\\SMime' => $vendorDir . '/symfony/mime/Crypto/SMime.php',
'Symfony\\Component\\Mime\\Crypto\\SMimeEncrypter' => $vendorDir . '/symfony/mime/Crypto/SMimeEncrypter.php',
'Symfony\\Component\\Mime\\Crypto\\SMimeSigner' => $vendorDir . '/symfony/mime/Crypto/SMimeSigner.php',
'Symfony\\Component\\Mime\\DependencyInjection\\AddMimeTypeGuesserPass' => $vendorDir . '/symfony/mime/DependencyInjection/AddMimeTypeGuesserPass.php',
'Symfony\\Component\\Mime\\DraftEmail' => $vendorDir . '/symfony/mime/DraftEmail.php',
'Symfony\\Component\\Mime\\Email' => $vendorDir . '/symfony/mime/Email.php',
'Symfony\\Component\\Mime\\Encoder\\AddressEncoderInterface' => $vendorDir . '/symfony/mime/Encoder/AddressEncoderInterface.php',
'Symfony\\Component\\Mime\\Encoder\\Base64ContentEncoder' => $vendorDir . '/symfony/mime/Encoder/Base64ContentEncoder.php',
'Symfony\\Component\\Mime\\Encoder\\Base64Encoder' => $vendorDir . '/symfony/mime/Encoder/Base64Encoder.php',
'Symfony\\Component\\Mime\\Encoder\\Base64MimeHeaderEncoder' => $vendorDir . '/symfony/mime/Encoder/Base64MimeHeaderEncoder.php',
'Symfony\\Component\\Mime\\Encoder\\ContentEncoderInterface' => $vendorDir . '/symfony/mime/Encoder/ContentEncoderInterface.php',
'Symfony\\Component\\Mime\\Encoder\\EightBitContentEncoder' => $vendorDir . '/symfony/mime/Encoder/EightBitContentEncoder.php',
'Symfony\\Component\\Mime\\Encoder\\EncoderInterface' => $vendorDir . '/symfony/mime/Encoder/EncoderInterface.php',
'Symfony\\Component\\Mime\\Encoder\\IdnAddressEncoder' => $vendorDir . '/symfony/mime/Encoder/IdnAddressEncoder.php',
'Symfony\\Component\\Mime\\Encoder\\MimeHeaderEncoderInterface' => $vendorDir . '/symfony/mime/Encoder/MimeHeaderEncoderInterface.php',
'Symfony\\Component\\Mime\\Encoder\\QpContentEncoder' => $vendorDir . '/symfony/mime/Encoder/QpContentEncoder.php',
'Symfony\\Component\\Mime\\Encoder\\QpEncoder' => $vendorDir . '/symfony/mime/Encoder/QpEncoder.php',
'Symfony\\Component\\Mime\\Encoder\\QpMimeHeaderEncoder' => $vendorDir . '/symfony/mime/Encoder/QpMimeHeaderEncoder.php',
'Symfony\\Component\\Mime\\Encoder\\Rfc2231Encoder' => $vendorDir . '/symfony/mime/Encoder/Rfc2231Encoder.php',
'Symfony\\Component\\Mime\\Exception\\AddressEncoderException' => $vendorDir . '/symfony/mime/Exception/AddressEncoderException.php',
'Symfony\\Component\\Mime\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/mime/Exception/ExceptionInterface.php',
'Symfony\\Component\\Mime\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/mime/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Mime\\Exception\\LogicException' => $vendorDir . '/symfony/mime/Exception/LogicException.php',
'Symfony\\Component\\Mime\\Exception\\RfcComplianceException' => $vendorDir . '/symfony/mime/Exception/RfcComplianceException.php',
'Symfony\\Component\\Mime\\Exception\\RuntimeException' => $vendorDir . '/symfony/mime/Exception/RuntimeException.php',
'Symfony\\Component\\Mime\\FileBinaryMimeTypeGuesser' => $vendorDir . '/symfony/mime/FileBinaryMimeTypeGuesser.php',
'Symfony\\Component\\Mime\\FileinfoMimeTypeGuesser' => $vendorDir . '/symfony/mime/FileinfoMimeTypeGuesser.php',
'Symfony\\Component\\Mime\\Header\\AbstractHeader' => $vendorDir . '/symfony/mime/Header/AbstractHeader.php',
'Symfony\\Component\\Mime\\Header\\DateHeader' => $vendorDir . '/symfony/mime/Header/DateHeader.php',
'Symfony\\Component\\Mime\\Header\\HeaderInterface' => $vendorDir . '/symfony/mime/Header/HeaderInterface.php',
'Symfony\\Component\\Mime\\Header\\Headers' => $vendorDir . '/symfony/mime/Header/Headers.php',
'Symfony\\Component\\Mime\\Header\\IdentificationHeader' => $vendorDir . '/symfony/mime/Header/IdentificationHeader.php',
'Symfony\\Component\\Mime\\Header\\MailboxHeader' => $vendorDir . '/symfony/mime/Header/MailboxHeader.php',
'Symfony\\Component\\Mime\\Header\\MailboxListHeader' => $vendorDir . '/symfony/mime/Header/MailboxListHeader.php',
'Symfony\\Component\\Mime\\Header\\ParameterizedHeader' => $vendorDir . '/symfony/mime/Header/ParameterizedHeader.php',
'Symfony\\Component\\Mime\\Header\\PathHeader' => $vendorDir . '/symfony/mime/Header/PathHeader.php',
'Symfony\\Component\\Mime\\Header\\UnstructuredHeader' => $vendorDir . '/symfony/mime/Header/UnstructuredHeader.php',
'Symfony\\Component\\Mime\\HtmlToTextConverter\\DefaultHtmlToTextConverter' => $vendorDir . '/symfony/mime/HtmlToTextConverter/DefaultHtmlToTextConverter.php',
'Symfony\\Component\\Mime\\HtmlToTextConverter\\HtmlToTextConverterInterface' => $vendorDir . '/symfony/mime/HtmlToTextConverter/HtmlToTextConverterInterface.php',
'Symfony\\Component\\Mime\\HtmlToTextConverter\\LeagueHtmlToMarkdownConverter' => $vendorDir . '/symfony/mime/HtmlToTextConverter/LeagueHtmlToMarkdownConverter.php',
'Symfony\\Component\\Mime\\Message' => $vendorDir . '/symfony/mime/Message.php',
'Symfony\\Component\\Mime\\MessageConverter' => $vendorDir . '/symfony/mime/MessageConverter.php',
'Symfony\\Component\\Mime\\MimeTypeGuesserInterface' => $vendorDir . '/symfony/mime/MimeTypeGuesserInterface.php',
'Symfony\\Component\\Mime\\MimeTypes' => $vendorDir . '/symfony/mime/MimeTypes.php',
'Symfony\\Component\\Mime\\MimeTypesInterface' => $vendorDir . '/symfony/mime/MimeTypesInterface.php',
'Symfony\\Component\\Mime\\Part\\AbstractMultipartPart' => $vendorDir . '/symfony/mime/Part/AbstractMultipartPart.php',
'Symfony\\Component\\Mime\\Part\\AbstractPart' => $vendorDir . '/symfony/mime/Part/AbstractPart.php',
'Symfony\\Component\\Mime\\Part\\DataPart' => $vendorDir . '/symfony/mime/Part/DataPart.php',
'Symfony\\Component\\Mime\\Part\\File' => $vendorDir . '/symfony/mime/Part/File.php',
'Symfony\\Component\\Mime\\Part\\MessagePart' => $vendorDir . '/symfony/mime/Part/MessagePart.php',
'Symfony\\Component\\Mime\\Part\\Multipart\\AlternativePart' => $vendorDir . '/symfony/mime/Part/Multipart/AlternativePart.php',
'Symfony\\Component\\Mime\\Part\\Multipart\\DigestPart' => $vendorDir . '/symfony/mime/Part/Multipart/DigestPart.php',
'Symfony\\Component\\Mime\\Part\\Multipart\\FormDataPart' => $vendorDir . '/symfony/mime/Part/Multipart/FormDataPart.php',
'Symfony\\Component\\Mime\\Part\\Multipart\\MixedPart' => $vendorDir . '/symfony/mime/Part/Multipart/MixedPart.php',
'Symfony\\Component\\Mime\\Part\\Multipart\\RelatedPart' => $vendorDir . '/symfony/mime/Part/Multipart/RelatedPart.php',
'Symfony\\Component\\Mime\\Part\\SMimePart' => $vendorDir . '/symfony/mime/Part/SMimePart.php',
'Symfony\\Component\\Mime\\Part\\TextPart' => $vendorDir . '/symfony/mime/Part/TextPart.php',
'Symfony\\Component\\Mime\\RawMessage' => $vendorDir . '/symfony/mime/RawMessage.php',
'Symfony\\Component\\Mime\\Test\\Constraint\\EmailAddressContains' => $vendorDir . '/symfony/mime/Test/Constraint/EmailAddressContains.php',
'Symfony\\Component\\Mime\\Test\\Constraint\\EmailAttachmentCount' => $vendorDir . '/symfony/mime/Test/Constraint/EmailAttachmentCount.php',
'Symfony\\Component\\Mime\\Test\\Constraint\\EmailHasHeader' => $vendorDir . '/symfony/mime/Test/Constraint/EmailHasHeader.php',
'Symfony\\Component\\Mime\\Test\\Constraint\\EmailHeaderSame' => $vendorDir . '/symfony/mime/Test/Constraint/EmailHeaderSame.php',
'Symfony\\Component\\Mime\\Test\\Constraint\\EmailHtmlBodyContains' => $vendorDir . '/symfony/mime/Test/Constraint/EmailHtmlBodyContains.php',
'Symfony\\Component\\Mime\\Test\\Constraint\\EmailSubjectContains' => $vendorDir . '/symfony/mime/Test/Constraint/EmailSubjectContains.php',
'Symfony\\Component\\Mime\\Test\\Constraint\\EmailTextBodyContains' => $vendorDir . '/symfony/mime/Test/Constraint/EmailTextBodyContains.php',
'Symfony\\Component\\Translation\\CatalogueMetadataAwareInterface' => $vendorDir . '/symfony/translation/CatalogueMetadataAwareInterface.php',
'Symfony\\Component\\Translation\\Catalogue\\AbstractOperation' => $vendorDir . '/symfony/translation/Catalogue/AbstractOperation.php',
'Symfony\\Component\\Translation\\Catalogue\\MergeOperation' => $vendorDir . '/symfony/translation/Catalogue/MergeOperation.php',
'Symfony\\Component\\Translation\\Catalogue\\OperationInterface' => $vendorDir . '/symfony/translation/Catalogue/OperationInterface.php',
'Symfony\\Component\\Translation\\Catalogue\\TargetOperation' => $vendorDir . '/symfony/translation/Catalogue/TargetOperation.php',
'Symfony\\Component\\Translation\\Command\\TranslationLintCommand' => $vendorDir . '/symfony/translation/Command/TranslationLintCommand.php',
'Symfony\\Component\\Translation\\Command\\TranslationPullCommand' => $vendorDir . '/symfony/translation/Command/TranslationPullCommand.php',
'Symfony\\Component\\Translation\\Command\\TranslationPushCommand' => $vendorDir . '/symfony/translation/Command/TranslationPushCommand.php',
'Symfony\\Component\\Translation\\Command\\TranslationTrait' => $vendorDir . '/symfony/translation/Command/TranslationTrait.php',
'Symfony\\Component\\Translation\\Command\\XliffLintCommand' => $vendorDir . '/symfony/translation/Command/XliffLintCommand.php',
'Symfony\\Component\\Translation\\DataCollectorTranslator' => $vendorDir . '/symfony/translation/DataCollectorTranslator.php',
'Symfony\\Component\\Translation\\DataCollector\\TranslationDataCollector' => $vendorDir . '/symfony/translation/DataCollector/TranslationDataCollector.php',
'Symfony\\Component\\Translation\\DependencyInjection\\DataCollectorTranslatorPass' => $vendorDir . '/symfony/translation/DependencyInjection/DataCollectorTranslatorPass.php',
'Symfony\\Component\\Translation\\DependencyInjection\\LoggingTranslatorPass' => $vendorDir . '/symfony/translation/DependencyInjection/LoggingTranslatorPass.php',
'Symfony\\Component\\Translation\\DependencyInjection\\TranslationDumperPass' => $vendorDir . '/symfony/translation/DependencyInjection/TranslationDumperPass.php',
'Symfony\\Component\\Translation\\DependencyInjection\\TranslationExtractorPass' => $vendorDir . '/symfony/translation/DependencyInjection/TranslationExtractorPass.php',
'Symfony\\Component\\Translation\\DependencyInjection\\TranslatorPass' => $vendorDir . '/symfony/translation/DependencyInjection/TranslatorPass.php',
'Symfony\\Component\\Translation\\DependencyInjection\\TranslatorPathsPass' => $vendorDir . '/symfony/translation/DependencyInjection/TranslatorPathsPass.php',
'Symfony\\Component\\Translation\\Dumper\\CsvFileDumper' => $vendorDir . '/symfony/translation/Dumper/CsvFileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\DumperInterface' => $vendorDir . '/symfony/translation/Dumper/DumperInterface.php',
'Symfony\\Component\\Translation\\Dumper\\FileDumper' => $vendorDir . '/symfony/translation/Dumper/FileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\IcuResFileDumper' => $vendorDir . '/symfony/translation/Dumper/IcuResFileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\IniFileDumper' => $vendorDir . '/symfony/translation/Dumper/IniFileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\JsonFileDumper' => $vendorDir . '/symfony/translation/Dumper/JsonFileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\MoFileDumper' => $vendorDir . '/symfony/translation/Dumper/MoFileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\PhpFileDumper' => $vendorDir . '/symfony/translation/Dumper/PhpFileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\PoFileDumper' => $vendorDir . '/symfony/translation/Dumper/PoFileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\QtFileDumper' => $vendorDir . '/symfony/translation/Dumper/QtFileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\XliffFileDumper' => $vendorDir . '/symfony/translation/Dumper/XliffFileDumper.php',
'Symfony\\Component\\Translation\\Dumper\\YamlFileDumper' => $vendorDir . '/symfony/translation/Dumper/YamlFileDumper.php',
'Symfony\\Component\\Translation\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/translation/Exception/ExceptionInterface.php',
'Symfony\\Component\\Translation\\Exception\\IncompleteDsnException' => $vendorDir . '/symfony/translation/Exception/IncompleteDsnException.php',
'Symfony\\Component\\Translation\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/translation/Exception/InvalidArgumentException.php',
'Symfony\\Component\\Translation\\Exception\\InvalidResourceException' => $vendorDir . '/symfony/translation/Exception/InvalidResourceException.php',
'Symfony\\Component\\Translation\\Exception\\LogicException' => $vendorDir . '/symfony/translation/Exception/LogicException.php',
'Symfony\\Component\\Translation\\Exception\\MissingRequiredOptionException' => $vendorDir . '/symfony/translation/Exception/MissingRequiredOptionException.php',
'Symfony\\Component\\Translation\\Exception\\NotFoundResourceException' => $vendorDir . '/symfony/translation/Exception/NotFoundResourceException.php',
'Symfony\\Component\\Translation\\Exception\\ProviderException' => $vendorDir . '/symfony/translation/Exception/ProviderException.php',
'Symfony\\Component\\Translation\\Exception\\ProviderExceptionInterface' => $vendorDir . '/symfony/translation/Exception/ProviderExceptionInterface.php',
'Symfony\\Component\\Translation\\Exception\\RuntimeException' => $vendorDir . '/symfony/translation/Exception/RuntimeException.php',
'Symfony\\Component\\Translation\\Exception\\UnsupportedSchemeException' => $vendorDir . '/symfony/translation/Exception/UnsupportedSchemeException.php',
'Symfony\\Component\\Translation\\Extractor\\AbstractFileExtractor' => $vendorDir . '/symfony/translation/Extractor/AbstractFileExtractor.php',
'Symfony\\Component\\Translation\\Extractor\\ChainExtractor' => $vendorDir . '/symfony/translation/Extractor/ChainExtractor.php',
'Symfony\\Component\\Translation\\Extractor\\ExtractorInterface' => $vendorDir . '/symfony/translation/Extractor/ExtractorInterface.php',
'Symfony\\Component\\Translation\\Extractor\\PhpAstExtractor' => $vendorDir . '/symfony/translation/Extractor/PhpAstExtractor.php',
'Symfony\\Component\\Translation\\Extractor\\Visitor\\AbstractVisitor' => $vendorDir . '/symfony/translation/Extractor/Visitor/AbstractVisitor.php',
'Symfony\\Component\\Translation\\Extractor\\Visitor\\ConstraintVisitor' => $vendorDir . '/symfony/translation/Extractor/Visitor/ConstraintVisitor.php',
'Symfony\\Component\\Translation\\Extractor\\Visitor\\TransMethodVisitor' => $vendorDir . '/symfony/translation/Extractor/Visitor/TransMethodVisitor.php',
'Symfony\\Component\\Translation\\Extractor\\Visitor\\TranslatableMessageVisitor' => $vendorDir . '/symfony/translation/Extractor/Visitor/TranslatableMessageVisitor.php',
'Symfony\\Component\\Translation\\Formatter\\IntlFormatter' => $vendorDir . '/symfony/translation/Formatter/IntlFormatter.php',
'Symfony\\Component\\Translation\\Formatter\\IntlFormatterInterface' => $vendorDir . '/symfony/translation/Formatter/IntlFormatterInterface.php',
'Symfony\\Component\\Translation\\Formatter\\MessageFormatter' => $vendorDir . '/symfony/translation/Formatter/MessageFormatter.php',
'Symfony\\Component\\Translation\\Formatter\\MessageFormatterInterface' => $vendorDir . '/symfony/translation/Formatter/MessageFormatterInterface.php',
'Symfony\\Component\\Translation\\IdentityTranslator' => $vendorDir . '/symfony/translation/IdentityTranslator.php',
'Symfony\\Component\\Translation\\Loader\\ArrayLoader' => $vendorDir . '/symfony/translation/Loader/ArrayLoader.php',
'Symfony\\Component\\Translation\\Loader\\CsvFileLoader' => $vendorDir . '/symfony/translation/Loader/CsvFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\FileLoader' => $vendorDir . '/symfony/translation/Loader/FileLoader.php',
'Symfony\\Component\\Translation\\Loader\\IcuDatFileLoader' => $vendorDir . '/symfony/translation/Loader/IcuDatFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\IcuResFileLoader' => $vendorDir . '/symfony/translation/Loader/IcuResFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\IniFileLoader' => $vendorDir . '/symfony/translation/Loader/IniFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\JsonFileLoader' => $vendorDir . '/symfony/translation/Loader/JsonFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\LoaderInterface' => $vendorDir . '/symfony/translation/Loader/LoaderInterface.php',
'Symfony\\Component\\Translation\\Loader\\MoFileLoader' => $vendorDir . '/symfony/translation/Loader/MoFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\PhpFileLoader' => $vendorDir . '/symfony/translation/Loader/PhpFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\PoFileLoader' => $vendorDir . '/symfony/translation/Loader/PoFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\QtFileLoader' => $vendorDir . '/symfony/translation/Loader/QtFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\XliffFileLoader' => $vendorDir . '/symfony/translation/Loader/XliffFileLoader.php',
'Symfony\\Component\\Translation\\Loader\\YamlFileLoader' => $vendorDir . '/symfony/translation/Loader/YamlFileLoader.php',
'Symfony\\Component\\Translation\\LocaleSwitcher' => $vendorDir . '/symfony/translation/LocaleSwitcher.php',
'Symfony\\Component\\Translation\\LoggingTranslator' => $vendorDir . '/symfony/translation/LoggingTranslator.php',
'Symfony\\Component\\Translation\\MessageCatalogue' => $vendorDir . '/symfony/translation/MessageCatalogue.php',
'Symfony\\Component\\Translation\\MessageCatalogueInterface' => $vendorDir . '/symfony/translation/MessageCatalogueInterface.php',
'Symfony\\Component\\Translation\\MetadataAwareInterface' => $vendorDir . '/symfony/translation/MetadataAwareInterface.php',
'Symfony\\Component\\Translation\\Provider\\AbstractProviderFactory' => $vendorDir . '/symfony/translation/Provider/AbstractProviderFactory.php',
'Symfony\\Component\\Translation\\Provider\\Dsn' => $vendorDir . '/symfony/translation/Provider/Dsn.php',
'Symfony\\Component\\Translation\\Provider\\FilteringProvider' => $vendorDir . '/symfony/translation/Provider/FilteringProvider.php',
'Symfony\\Component\\Translation\\Provider\\NullProvider' => $vendorDir . '/symfony/translation/Provider/NullProvider.php',
'Symfony\\Component\\Translation\\Provider\\NullProviderFactory' => $vendorDir . '/symfony/translation/Provider/NullProviderFactory.php',
'Symfony\\Component\\Translation\\Provider\\ProviderFactoryInterface' => $vendorDir . '/symfony/translation/Provider/ProviderFactoryInterface.php',
'Symfony\\Component\\Translation\\Provider\\ProviderInterface' => $vendorDir . '/symfony/translation/Provider/ProviderInterface.php',
'Symfony\\Component\\Translation\\Provider\\TranslationProviderCollection' => $vendorDir . '/symfony/translation/Provider/TranslationProviderCollection.php',
'Symfony\\Component\\Translation\\Provider\\TranslationProviderCollectionFactory' => $vendorDir . '/symfony/translation/Provider/TranslationProviderCollectionFactory.php',
'Symfony\\Component\\Translation\\PseudoLocalizationTranslator' => $vendorDir . '/symfony/translation/PseudoLocalizationTranslator.php',
'Symfony\\Component\\Translation\\Reader\\TranslationReader' => $vendorDir . '/symfony/translation/Reader/TranslationReader.php',
'Symfony\\Component\\Translation\\Reader\\TranslationReaderInterface' => $vendorDir . '/symfony/translation/Reader/TranslationReaderInterface.php',
'Symfony\\Component\\Translation\\StaticMessage' => $vendorDir . '/symfony/translation/StaticMessage.php',
'Symfony\\Component\\Translation\\Test\\AbstractProviderFactoryTestCase' => $vendorDir . '/symfony/translation/Test/AbstractProviderFactoryTestCase.php',
'Symfony\\Component\\Translation\\Test\\IncompleteDsnTestTrait' => $vendorDir . '/symfony/translation/Test/IncompleteDsnTestTrait.php',
'Symfony\\Component\\Translation\\Test\\ProviderFactoryTestCase' => $vendorDir . '/symfony/translation/Test/ProviderFactoryTestCase.php',
'Symfony\\Component\\Translation\\Test\\ProviderTestCase' => $vendorDir . '/symfony/translation/Test/ProviderTestCase.php',
'Symfony\\Component\\Translation\\TranslatableMessage' => $vendorDir . '/symfony/translation/TranslatableMessage.php',
'Symfony\\Component\\Translation\\Translator' => $vendorDir . '/symfony/translation/Translator.php',
'Symfony\\Component\\Translation\\TranslatorBag' => $vendorDir . '/symfony/translation/TranslatorBag.php',
'Symfony\\Component\\Translation\\TranslatorBagInterface' => $vendorDir . '/symfony/translation/TranslatorBagInterface.php',
'Symfony\\Component\\Translation\\Util\\ArrayConverter' => $vendorDir . '/symfony/translation/Util/ArrayConverter.php',
'Symfony\\Component\\Translation\\Util\\XliffUtils' => $vendorDir . '/symfony/translation/Util/XliffUtils.php',
'Symfony\\Component\\Translation\\Writer\\TranslationWriter' => $vendorDir . '/symfony/translation/Writer/TranslationWriter.php',
'Symfony\\Component\\Translation\\Writer\\TranslationWriterInterface' => $vendorDir . '/symfony/translation/Writer/TranslationWriterInterface.php',
'Symfony\\Contracts\\Translation\\LocaleAwareInterface' => $vendorDir . '/symfony/translation-contracts/LocaleAwareInterface.php',
'Symfony\\Contracts\\Translation\\TranslatableInterface' => $vendorDir . '/symfony/translation-contracts/TranslatableInterface.php',
'Symfony\\Contracts\\Translation\\TranslatorInterface' => $vendorDir . '/symfony/translation-contracts/TranslatorInterface.php',
'Symfony\\Contracts\\Translation\\TranslatorTrait' => $vendorDir . '/symfony/translation-contracts/TranslatorTrait.php',
'Symfony\\Polyfill\\Iconv\\Iconv' => $vendorDir . '/symfony/polyfill-iconv/Iconv.php',
'Symfony\\Polyfill\\Intl\\Idn\\Idn' => $vendorDir . '/symfony/polyfill-intl-idn/Idn.php',
'Symfony\\Polyfill\\Intl\\Idn\\Info' => $vendorDir . '/symfony/polyfill-intl-idn/Info.php',
'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\DisallowedRanges' => $vendorDir . '/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.php',
'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\Regex' => $vendorDir . '/symfony/polyfill-intl-idn/Resources/unidata/Regex.php',
'Symfony\\Polyfill\\Intl\\Normalizer\\Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Normalizer.php',
'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php',
'Symfony\\Polyfill\\Php80\\Php80' => $vendorDir . '/symfony/polyfill-php80/Php80.php',
'Symfony\\Polyfill\\Php80\\PhpToken' => $vendorDir . '/symfony/polyfill-php80/PhpToken.php',
'Symfony\\Polyfill\\Php83\\Php83' => $vendorDir . '/symfony/polyfill-php83/Php83.php',
'Symfony\\Polyfill\\Php84\\Php84' => $vendorDir . '/symfony/polyfill-php84/Php84.php',
'Symfony\\Polyfill\\Php85\\Php85' => $vendorDir . '/symfony/polyfill-php85/Php85.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
'ZBateson\\MailMimeParser\\Error' => $vendorDir . '/zbateson/mail-mime-parser/src/Error.php',
'ZBateson\\MailMimeParser\\ErrorBag' => $vendorDir . '/zbateson/mail-mime-parser/src/ErrorBag.php',
'ZBateson\\MailMimeParser\\Header\\AbstractHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/AbstractHeader.php',
'ZBateson\\MailMimeParser\\Header\\AddressHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/AddressHeader.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\AbstractConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/AbstractConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\AbstractGenericConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/AbstractGenericConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\AddressBaseConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/AddressBaseConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\AddressConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/AddressConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\AddressEmailConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/AddressEmailConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\AddressGroupConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/AddressGroupConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\CommentConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/CommentConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\DateConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/DateConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\GenericConsumerMimeLiteralPartService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/GenericConsumerMimeLiteralPartService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\GenericConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/GenericConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\IConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/IConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\IdBaseConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/IdBaseConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\IdConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/IdConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\ParameterConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/ParameterConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\ParameterNameValueConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/ParameterNameValueConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\ParameterValueConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/ParameterValueConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\QuotedStringConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/QuotedStringConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\QuotedStringMimeLiteralPartConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/QuotedStringMimeLiteralPartConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\QuotedStringMimeLiteralPartTokenSplitPatternTrait' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/QuotedStringMimeLiteralPartTokenSplitPatternTrait.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\ReceivedConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/ReceivedConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\Received\\DomainConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/Received/DomainConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\Received\\GenericReceivedConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/Received/GenericReceivedConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\Received\\ReceivedDateConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/Received/ReceivedDateConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\Consumer\\SubjectConsumerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Consumer/SubjectConsumerService.php',
'ZBateson\\MailMimeParser\\Header\\DateHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/DateHeader.php',
'ZBateson\\MailMimeParser\\Header\\GenericHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/GenericHeader.php',
'ZBateson\\MailMimeParser\\Header\\HeaderConsts' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/HeaderConsts.php',
'ZBateson\\MailMimeParser\\Header\\HeaderFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/HeaderFactory.php',
'ZBateson\\MailMimeParser\\Header\\IHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/IHeader.php',
'ZBateson\\MailMimeParser\\Header\\IHeaderPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/IHeaderPart.php',
'ZBateson\\MailMimeParser\\Header\\IdHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/IdHeader.php',
'ZBateson\\MailMimeParser\\Header\\MimeEncodedHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/MimeEncodedHeader.php',
'ZBateson\\MailMimeParser\\Header\\ParameterHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/ParameterHeader.php',
'ZBateson\\MailMimeParser\\Header\\Part\\AddressGroupPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/AddressGroupPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\AddressPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/AddressPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\CommentPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/CommentPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\ContainerPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/ContainerPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\DatePart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/DatePart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\HeaderPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/HeaderPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\HeaderPartFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/HeaderPartFactory.php',
'ZBateson\\MailMimeParser\\Header\\Part\\MimeToken' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/MimeToken.php',
'ZBateson\\MailMimeParser\\Header\\Part\\MimeTokenPartFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/MimeTokenPartFactory.php',
'ZBateson\\MailMimeParser\\Header\\Part\\NameValuePart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/NameValuePart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\ParameterPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/ParameterPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\QuotedLiteralPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/QuotedLiteralPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\ReceivedDomainPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/ReceivedDomainPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\ReceivedPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/ReceivedPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\SplitParameterPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/SplitParameterPart.php',
'ZBateson\\MailMimeParser\\Header\\Part\\SubjectToken' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/SubjectToken.php',
'ZBateson\\MailMimeParser\\Header\\Part\\Token' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/Part/Token.php',
'ZBateson\\MailMimeParser\\Header\\ReceivedHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/ReceivedHeader.php',
'ZBateson\\MailMimeParser\\Header\\SubjectHeader' => $vendorDir . '/zbateson/mail-mime-parser/src/Header/SubjectHeader.php',
'ZBateson\\MailMimeParser\\IErrorBag' => $vendorDir . '/zbateson/mail-mime-parser/src/IErrorBag.php',
'ZBateson\\MailMimeParser\\IMessage' => $vendorDir . '/zbateson/mail-mime-parser/src/IMessage.php',
'ZBateson\\MailMimeParser\\MailMimeParser' => $vendorDir . '/zbateson/mail-mime-parser/src/MailMimeParser.php',
'ZBateson\\MailMimeParser\\Message' => $vendorDir . '/zbateson/mail-mime-parser/src/Message.php',
'ZBateson\\MailMimeParser\\Message\\Factory\\IMessagePartFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Factory/IMessagePartFactory.php',
'ZBateson\\MailMimeParser\\Message\\Factory\\IMimePartFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Factory/IMimePartFactory.php',
'ZBateson\\MailMimeParser\\Message\\Factory\\IUUEncodedPartFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Factory/IUUEncodedPartFactory.php',
'ZBateson\\MailMimeParser\\Message\\Factory\\PartChildrenContainerFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Factory/PartChildrenContainerFactory.php',
'ZBateson\\MailMimeParser\\Message\\Factory\\PartHeaderContainerFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Factory/PartHeaderContainerFactory.php',
'ZBateson\\MailMimeParser\\Message\\Factory\\PartStreamContainerFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Factory/PartStreamContainerFactory.php',
'ZBateson\\MailMimeParser\\Message\\Helper\\AbstractHelper' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Helper/AbstractHelper.php',
'ZBateson\\MailMimeParser\\Message\\Helper\\GenericHelper' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Helper/GenericHelper.php',
'ZBateson\\MailMimeParser\\Message\\Helper\\MultipartHelper' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Helper/MultipartHelper.php',
'ZBateson\\MailMimeParser\\Message\\Helper\\PrivacyHelper' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/Helper/PrivacyHelper.php',
'ZBateson\\MailMimeParser\\Message\\IMessagePart' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/IMessagePart.php',
'ZBateson\\MailMimeParser\\Message\\IMimePart' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/IMimePart.php',
'ZBateson\\MailMimeParser\\Message\\IMultiPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/IMultiPart.php',
'ZBateson\\MailMimeParser\\Message\\IUUEncodedPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/IUUEncodedPart.php',
'ZBateson\\MailMimeParser\\Message\\MessagePart' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/MessagePart.php',
'ZBateson\\MailMimeParser\\Message\\MimePart' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/MimePart.php',
'ZBateson\\MailMimeParser\\Message\\MultiPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/MultiPart.php',
'ZBateson\\MailMimeParser\\Message\\NonMimePart' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/NonMimePart.php',
'ZBateson\\MailMimeParser\\Message\\PartChildrenContainer' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/PartChildrenContainer.php',
'ZBateson\\MailMimeParser\\Message\\PartFilter' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/PartFilter.php',
'ZBateson\\MailMimeParser\\Message\\PartHeaderContainer' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/PartHeaderContainer.php',
'ZBateson\\MailMimeParser\\Message\\PartStreamContainer' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/PartStreamContainer.php',
'ZBateson\\MailMimeParser\\Message\\UUEncodedPart' => $vendorDir . '/zbateson/mail-mime-parser/src/Message/UUEncodedPart.php',
'ZBateson\\MailMimeParser\\Parser\\AbstractParserService' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/AbstractParserService.php',
'ZBateson\\MailMimeParser\\Parser\\CompatibleParserNotFoundException' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/CompatibleParserNotFoundException.php',
'ZBateson\\MailMimeParser\\Parser\\HeaderParserService' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/HeaderParserService.php',
'ZBateson\\MailMimeParser\\Parser\\IParserService' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/IParserService.php',
'ZBateson\\MailMimeParser\\Parser\\MessageParserService' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/MessageParserService.php',
'ZBateson\\MailMimeParser\\Parser\\MimeParserService' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/MimeParserService.php',
'ZBateson\\MailMimeParser\\Parser\\NonMimeParserService' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/NonMimeParserService.php',
'ZBateson\\MailMimeParser\\Parser\\ParserManagerService' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/ParserManagerService.php',
'ZBateson\\MailMimeParser\\Parser\\PartBuilder' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/PartBuilder.php',
'ZBateson\\MailMimeParser\\Parser\\PartBuilderFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/PartBuilderFactory.php',
'ZBateson\\MailMimeParser\\Parser\\Part\\ParserPartChildrenContainer' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Part/ParserPartChildrenContainer.php',
'ZBateson\\MailMimeParser\\Parser\\Part\\ParserPartChildrenContainerFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Part/ParserPartChildrenContainerFactory.php',
'ZBateson\\MailMimeParser\\Parser\\Part\\ParserPartStreamContainer' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Part/ParserPartStreamContainer.php',
'ZBateson\\MailMimeParser\\Parser\\Part\\ParserPartStreamContainerFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Part/ParserPartStreamContainerFactory.php',
'ZBateson\\MailMimeParser\\Parser\\Part\\UUEncodedPartHeaderContainer' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Part/UUEncodedPartHeaderContainer.php',
'ZBateson\\MailMimeParser\\Parser\\Part\\UUEncodedPartHeaderContainerFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Part/UUEncodedPartHeaderContainerFactory.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserMessageProxy' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserMessageProxy.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserMessageProxyFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserMessageProxyFactory.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserMimePartProxy' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserMimePartProxy.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserMimePartProxyFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserMimePartProxyFactory.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserNonMimeMessageProxy' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserNonMimeMessageProxy.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserNonMimeMessageProxyFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserNonMimeMessageProxyFactory.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserPartProxy' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserPartProxy.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserPartProxyFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserPartProxyFactory.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserUUEncodedPartProxy' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserUUEncodedPartProxy.php',
'ZBateson\\MailMimeParser\\Parser\\Proxy\\ParserUUEncodedPartProxyFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Parser/Proxy/ParserUUEncodedPartProxyFactory.php',
'ZBateson\\MailMimeParser\\Stream\\HeaderStream' => $vendorDir . '/zbateson/mail-mime-parser/src/Stream/HeaderStream.php',
'ZBateson\\MailMimeParser\\Stream\\MessagePartStream' => $vendorDir . '/zbateson/mail-mime-parser/src/Stream/MessagePartStream.php',
'ZBateson\\MailMimeParser\\Stream\\MessagePartStreamDecorator' => $vendorDir . '/zbateson/mail-mime-parser/src/Stream/MessagePartStreamDecorator.php',
'ZBateson\\MailMimeParser\\Stream\\MessagePartStreamReadException' => $vendorDir . '/zbateson/mail-mime-parser/src/Stream/MessagePartStreamReadException.php',
'ZBateson\\MailMimeParser\\Stream\\StreamFactory' => $vendorDir . '/zbateson/mail-mime-parser/src/Stream/StreamFactory.php',
'ZBateson\\MbWrapper\\MbWrapper' => $vendorDir . '/zbateson/mb-wrapper/src/MbWrapper.php',
'ZBateson\\MbWrapper\\UnsupportedCharsetException' => $vendorDir . '/zbateson/mb-wrapper/src/UnsupportedCharsetException.php',
'ZBateson\\StreamDecorators\\Base64Stream' => $vendorDir . '/zbateson/stream-decorators/src/Base64Stream.php',
'ZBateson\\StreamDecorators\\CharsetStream' => $vendorDir . '/zbateson/stream-decorators/src/CharsetStream.php',
'ZBateson\\StreamDecorators\\ChunkSplitStream' => $vendorDir . '/zbateson/stream-decorators/src/ChunkSplitStream.php',
'ZBateson\\StreamDecorators\\DecoratedCachingStream' => $vendorDir . '/zbateson/stream-decorators/src/DecoratedCachingStream.php',
'ZBateson\\StreamDecorators\\NonClosingStream' => $vendorDir . '/zbateson/stream-decorators/src/NonClosingStream.php',
'ZBateson\\StreamDecorators\\PregReplaceFilterStream' => $vendorDir . '/zbateson/stream-decorators/src/PregReplaceFilterStream.php',
'ZBateson\\StreamDecorators\\QuotedPrintableStream' => $vendorDir . '/zbateson/stream-decorators/src/QuotedPrintableStream.php',
'ZBateson\\StreamDecorators\\SeekingLimitStream' => $vendorDir . '/zbateson/stream-decorators/src/SeekingLimitStream.php',
'ZBateson\\StreamDecorators\\TellZeroStream' => $vendorDir . '/zbateson/stream-decorators/src/TellZeroStream.php',
'ZBateson\\StreamDecorators\\UUStream' => $vendorDir . '/zbateson/stream-decorators/src/UUStream.php',
);

24
libs/vendor/composer/autoload_files.php vendored Normal file
View File

@@ -0,0 +1,24 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
'2203a247e6fda86070a5e4e07aed533a' => $vendorDir . '/symfony/clock/Resources/now.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'b33e3d135e5d9e47d845c576147bda89' => $vendorDir . '/php-di/php-di/src/functions.php',
'606a39d89246991a373564698c2d8383' => $vendorDir . '/symfony/polyfill-php85/bootstrap.php',
'9d2b9fc6db0f153a0a149fefb182415e' => $vendorDir . '/symfony/polyfill-php84/bootstrap.php',
'23f09fe3194f8c2f70923f90d6702129' => $vendorDir . '/illuminate/collections/functions.php',
'60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php',
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

40
libs/vendor/composer/autoload_psr4.php vendored Normal file
View File

@@ -0,0 +1,40 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'ZBateson\\StreamDecorators\\' => array($vendorDir . '/zbateson/stream-decorators/src'),
'ZBateson\\MbWrapper\\' => array($vendorDir . '/zbateson/mb-wrapper/src'),
'ZBateson\\MailMimeParser\\' => array($vendorDir . '/zbateson/mail-mime-parser/src'),
'Symfony\\Polyfill\\Php85\\' => array($vendorDir . '/symfony/polyfill-php85'),
'Symfony\\Polyfill\\Php84\\' => array($vendorDir . '/symfony/polyfill-php84'),
'Symfony\\Polyfill\\Php83\\' => array($vendorDir . '/symfony/polyfill-php83'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'),
'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
'Symfony\\Component\\Mime\\' => array($vendorDir . '/symfony/mime'),
'Symfony\\Component\\Clock\\' => array($vendorDir . '/symfony/clock'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
'Laravel\\SerializableClosure\\' => array($vendorDir . '/laravel/serializable-closure/src'),
'Invoker\\' => array($vendorDir . '/php-di/invoker/src'),
'Illuminate\\Support\\' => array($vendorDir . '/illuminate/macroable', $vendorDir . '/illuminate/conditionable', $vendorDir . '/illuminate/collections'),
'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/src'),
'DirectoryTree\\ImapEngine\\' => array($vendorDir . '/directorytree/imapengine/src'),
'DI\\' => array($vendorDir . '/php-di/php-di/src'),
'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'),
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
);

80
libs/vendor/composer/autoload_real.php vendored Normal file
View File

@@ -0,0 +1,80 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitbadf1d01c367c06fb591106ea3486c30
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInitbadf1d01c367c06fb591106ea3486c30', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInitbadf1d01c367c06fb591106ea3486c30', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitbadf1d01c367c06fb591106ea3486c30::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitbadf1d01c367c06fb591106ea3486c30::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequirebadf1d01c367c06fb591106ea3486c30($fileIdentifier, $file);
}
return $loader;
}
}
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequirebadf1d01c367c06fb591106ea3486c30($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}

1167
libs/vendor/composer/autoload_static.php vendored Normal file

File diff suppressed because it is too large Load Diff

2649
libs/vendor/composer/installed.json vendored Normal file

File diff suppressed because it is too large Load Diff

377
libs/vendor/composer/installed.php vendored Normal file
View File

@@ -0,0 +1,377 @@
<?php return array(
'root' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '171a0d38f876a1af3db48a83e312fb52d3bdac75',
'name' => '__root__',
'dev' => false,
),
'versions' => array(
'__root__' => array(
'pretty_version' => 'dev-develop',
'version' => 'dev-develop',
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '171a0d38f876a1af3db48a83e312fb52d3bdac75',
'dev_requirement' => false,
),
'carbonphp/carbon-doctrine-types' => array(
'pretty_version' => '3.2.0',
'version' => '3.2.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../carbonphp/carbon-doctrine-types',
'aliases' => array(),
'reference' => '18ba5ddfec8976260ead6e866180bd5d2f71aa1d',
'dev_requirement' => false,
),
'directorytree/imapengine' => array(
'pretty_version' => 'v1.25.0',
'version' => '1.25.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../directorytree/imapengine',
'aliases' => array(),
'reference' => 'ac8a4d028334c2d3a4bc8fd975317a75cd968a47',
'dev_requirement' => false,
),
'doctrine/lexer' => array(
'pretty_version' => '3.0.1',
'version' => '3.0.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../doctrine/lexer',
'aliases' => array(),
'reference' => '31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd',
'dev_requirement' => false,
),
'egulias/email-validator' => array(
'pretty_version' => '4.0.4',
'version' => '4.0.4.0',
'type' => 'library',
'install_path' => __DIR__ . '/../egulias/email-validator',
'aliases' => array(),
'reference' => 'd42c8731f0624ad6bdc8d3e5e9a4524f68801cfa',
'dev_requirement' => false,
),
'guzzlehttp/psr7' => array(
'pretty_version' => '2.12.3',
'version' => '2.12.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
'aliases' => array(),
'reference' => '7ec62dc3f44aa218487dbed81a9bf9bc647be55d',
'dev_requirement' => false,
),
'illuminate/collections' => array(
'pretty_version' => 'v12.62.0',
'version' => '12.62.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/collections',
'aliases' => array(),
'reference' => '83313b009c4afb6f02dbc090bdb67809756eefa2',
'dev_requirement' => false,
),
'illuminate/conditionable' => array(
'pretty_version' => 'v12.62.0',
'version' => '12.62.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/conditionable',
'aliases' => array(),
'reference' => 'ec677967c1f2faf90b8428919124d2184a4c9b49',
'dev_requirement' => false,
),
'illuminate/contracts' => array(
'pretty_version' => 'v12.62.0',
'version' => '12.62.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/contracts',
'aliases' => array(),
'reference' => 'c16fd7ba7d8e6b8f336639ceb6b7e1ff6ed0efb5',
'dev_requirement' => false,
),
'illuminate/macroable' => array(
'pretty_version' => 'v12.62.0',
'version' => '12.62.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../illuminate/macroable',
'aliases' => array(),
'reference' => 'e295d62d89dcdb87e2b1bd70dd14d074a0ed73cc',
'dev_requirement' => false,
),
'laravel/serializable-closure' => array(
'pretty_version' => 'v2.0.13',
'version' => '2.0.13.0',
'type' => 'library',
'install_path' => __DIR__ . '/../laravel/serializable-closure',
'aliases' => array(),
'reference' => 'b566ee0dd251f3c4078bed003a7ce015f5ea6dce',
'dev_requirement' => false,
),
'nesbot/carbon' => array(
'pretty_version' => '3.13.0',
'version' => '3.13.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../nesbot/carbon',
'aliases' => array(),
'reference' => '40f6618f052df16b545f626fbf9a878e6497d16a',
'dev_requirement' => false,
),
'php-di/invoker' => array(
'pretty_version' => '2.3.7',
'version' => '2.3.7.0',
'type' => 'library',
'install_path' => __DIR__ . '/../php-di/invoker',
'aliases' => array(),
'reference' => '3c1ddfdef181431fbc4be83378f6d036d59e81e1',
'dev_requirement' => false,
),
'php-di/php-di' => array(
'pretty_version' => '7.1.1',
'version' => '7.1.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../php-di/php-di',
'aliases' => array(),
'reference' => 'f88054cc052e40dbe7b383c8817c19442d480352',
'dev_requirement' => false,
),
'psr/clock' => array(
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/clock',
'aliases' => array(),
'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d',
'dev_requirement' => false,
),
'psr/clock-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '1.0',
),
),
'psr/container' => array(
'pretty_version' => '2.0.2',
'version' => '2.0.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/container',
'aliases' => array(),
'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963',
'dev_requirement' => false,
),
'psr/container-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '^1.0',
),
),
'psr/http-factory' => array(
'pretty_version' => '1.1.0',
'version' => '1.1.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-factory',
'aliases' => array(),
'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
'dev_requirement' => false,
),
'psr/http-factory-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '1.0',
),
),
'psr/http-message' => array(
'pretty_version' => '2.0',
'version' => '2.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-message',
'aliases' => array(),
'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
'dev_requirement' => false,
),
'psr/http-message-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '1.0',
),
),
'psr/log' => array(
'pretty_version' => '3.0.2',
'version' => '3.0.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/log',
'aliases' => array(),
'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
'dev_requirement' => false,
),
'psr/simple-cache' => array(
'pretty_version' => '3.0.0',
'version' => '3.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/simple-cache',
'aliases' => array(),
'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865',
'dev_requirement' => false,
),
'ralouphie/getallheaders' => array(
'pretty_version' => '3.0.3',
'version' => '3.0.3.0',
'type' => 'library',
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
'aliases' => array(),
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
'dev_requirement' => false,
),
'symfony/clock' => array(
'pretty_version' => 'v7.4.8',
'version' => '7.4.8.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/clock',
'aliases' => array(),
'reference' => '674fa3b98e21531dd040e613479f5f6fa8f32111',
'dev_requirement' => false,
),
'symfony/deprecation-contracts' => array(
'pretty_version' => 'v3.7.0',
'version' => '3.7.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(),
'reference' => '50f59d1f3ca46d41ac911f97a78626b6756af35b',
'dev_requirement' => false,
),
'symfony/mime' => array(
'pretty_version' => 'v7.4.13',
'version' => '7.4.13.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/mime',
'aliases' => array(),
'reference' => 'a845722765c4f6b2ce88beaf4f4479975b186770',
'dev_requirement' => false,
),
'symfony/polyfill-iconv' => array(
'pretty_version' => 'v1.37.0',
'version' => '1.37.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-iconv',
'aliases' => array(),
'reference' => '2c5729fd241b4b22f6e4b436bc3354a4f262df57',
'dev_requirement' => false,
),
'symfony/polyfill-intl-idn' => array(
'pretty_version' => 'v1.38.1',
'version' => '1.38.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-idn',
'aliases' => array(),
'reference' => 'dc21118016c039a66235cf93d96b435ffb282412',
'dev_requirement' => false,
),
'symfony/polyfill-intl-normalizer' => array(
'pretty_version' => 'v1.38.0',
'version' => '1.38.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-intl-normalizer',
'aliases' => array(),
'reference' => '2d446c214bdbe5b71bde5011b060a05fece3ae6b',
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.38.2',
'version' => '1.38.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'reference' => 'd3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6',
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.37.0',
'version' => '1.37.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'reference' => 'dfb55726c3a76ea3b6459fcfda1ec2d80a682411',
'dev_requirement' => false,
),
'symfony/polyfill-php83' => array(
'pretty_version' => 'v1.38.2',
'version' => '1.38.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php83',
'aliases' => array(),
'reference' => '796a26abb75ce49f3a84433cd81bf1009d73d5f8',
'dev_requirement' => false,
),
'symfony/polyfill-php84' => array(
'pretty_version' => 'v1.38.1',
'version' => '1.38.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php84',
'aliases' => array(),
'reference' => 'f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa',
'dev_requirement' => false,
),
'symfony/polyfill-php85' => array(
'pretty_version' => 'v1.38.1',
'version' => '1.38.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php85',
'aliases' => array(),
'reference' => 'ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1',
'dev_requirement' => false,
),
'symfony/translation' => array(
'pretty_version' => 'v7.4.10',
'version' => '7.4.10.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/translation',
'aliases' => array(),
'reference' => 'ada7578c30dd5feaa8259cff3e885069ea81ddde',
'dev_requirement' => false,
),
'symfony/translation-contracts' => array(
'pretty_version' => 'v3.7.0',
'version' => '3.7.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/translation-contracts',
'aliases' => array(),
'reference' => '0ab302977a952b42fd51475c4ebac81f8da0a95d',
'dev_requirement' => false,
),
'symfony/translation-implementation' => array(
'dev_requirement' => false,
'provided' => array(
0 => '2.3|3.0',
),
),
'zbateson/mail-mime-parser' => array(
'pretty_version' => '3.0.6',
'version' => '3.0.6.0',
'type' => 'library',
'install_path' => __DIR__ . '/../zbateson/mail-mime-parser',
'aliases' => array(),
'reference' => '395c406cc1c5d1eb171d9decb0a3b509e8bd6bc0',
'dev_requirement' => false,
),
'zbateson/mb-wrapper' => array(
'pretty_version' => '2.0.1',
'version' => '2.0.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../zbateson/mb-wrapper',
'aliases' => array(),
'reference' => '50a14c0c9537f978a61cde9fdc192a0267cc9cff',
'dev_requirement' => false,
),
'zbateson/stream-decorators' => array(
'pretty_version' => '2.1.1',
'version' => '2.1.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../zbateson/stream-decorators',
'aliases' => array(),
'reference' => '32a2a62fb0f26313395c996ebd658d33c3f9c4e5',
'dev_requirement' => false,
),
),
);

26
libs/vendor/composer/platform_check.php vendored Normal file
View File

@@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 80200)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

View File

@@ -0,0 +1,46 @@
{
"name": "directorytree/imapengine",
"type": "library",
"description": "A fully-featured IMAP library -- without the PHP extension",
"keywords": [
"imap",
"mail",
"engine"
],
"homepage": "https://github.com/directorytree/imapengine",
"license": "MIT",
"authors": [
{
"name": "Steve Bauman",
"email": "steven_bauman@outlook.com",
"role": "Developer"
}
],
"require": {
"php": "^8.1",
"symfony/mime": ">=6.0",
"nesbot/carbon": ">=2.0",
"illuminate/collections": ">=9.0",
"zbateson/mail-mime-parser": "^3.0",
"egulias/email-validator": "^4.0"
},
"require-dev": {
"spatie/ray": "^1.0",
"pestphp/pest": "^2.0|^3.0|^4.0"
},
"autoload": {
"psr-4": {
"DirectoryTree\\ImapEngine\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests"
}
},
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
}
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace DirectoryTree\ImapEngine;
use DirectoryTree\ImapEngine\Support\Str;
use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;
class Address implements Arrayable, JsonSerializable
{
/**
* Constructor.
*/
public function __construct(
protected string $email,
protected string $name,
) {
$this->name = Str::decodeMimeHeader($this->name);
}
/**
* Get the address's email.
*/
public function email(): string
{
return $this->email;
}
/**
* Get the address's name.
*/
public function name(): string
{
return $this->name;
}
/**
* Get the array representation of the address.
*/
public function toArray(): array
{
return [
'email' => $this->email,
'name' => $this->name,
];
}
/**
* Get the JSON representation of the address.
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}

View File

@@ -0,0 +1,174 @@
<?php
namespace DirectoryTree\ImapEngine;
use GuzzleHttp\Psr7\Utils;
use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;
use Psr\Http\Message\StreamInterface;
use Symfony\Component\Mime\MimeTypes;
use ZBateson\MailMimeParser\Message\IMessagePart;
class Attachment implements Arrayable, JsonSerializable
{
/**
* Constructor.
*/
public function __construct(
protected ?string $filename,
protected ?string $contentId,
protected string $contentType,
protected ?string $contentDisposition,
protected StreamInterface $contentStream,
) {}
/**
* Get attachments from a parsed message.
*
* @return Attachment[]
*/
public static function parsed(MessageInterface $message): array
{
$attachments = [];
foreach ($message->parse()->getAllAttachmentParts() as $part) {
if (static::isForwardedMessage($part)) {
$attachments = array_merge($attachments, (new FileMessage($part->getContent()))->attachments());
} else {
$attachments[] = new Attachment(
$part->getFilename(),
$part->getContentId(),
$part->getContentType(),
$part->getContentDisposition(),
$part->getBinaryContentStream() ?? Utils::streamFor(''),
);
}
}
return $attachments;
}
/**
* Get attachments from a message's body structure using lazy streams.
*
* @return Attachment[]
*/
public static function lazy(Message $message): array
{
$attachments = [];
foreach ($message->bodyStructure(fetch: true)?->attachments() ?? [] as $part) {
$attachments[] = new Attachment(
$part->filename(),
$part->id(),
$part->contentType(),
$part->disposition()?->type()?->value,
new Support\LazyBodyPartStream($message, $part),
);
}
return $attachments;
}
/**
* Determine if the attachment should be treated as an embedded forwarded message.
*/
protected static function isForwardedMessage(IMessagePart $part): bool
{
return empty($part->getFilename())
&& strtolower((string) $part->getContentType()) === 'message/rfc822'
&& strtolower((string) $part->getContentDisposition()) !== 'attachment';
}
/**
* Get the attachment's filename.
*/
public function filename(): ?string
{
return $this->filename;
}
/**
* Get the attachment's content ID.
*/
public function contentId(): ?string
{
return $this->contentId;
}
/**
* Get the attachment's content type.
*/
public function contentType(): string
{
return $this->contentType;
}
/**
* Get the attachment's content disposition.
*/
public function contentDisposition(): string
{
return $this->contentDisposition;
}
/**
* Get the attachment's contents.
*/
public function contents(): string
{
return $this->contentStream->getContents();
}
/**
* Get the attachment's content stream.
*/
public function contentStream(): StreamInterface
{
return $this->contentStream;
}
/**
* Save the attachment to a file.
*/
public function save(string $path): false|int
{
return file_put_contents($path, $this->contents());
}
/**
* Get the attachment's extension.
*/
public function extension(): ?string
{
if ($ext = pathinfo($this->filename ?? '', PATHINFO_EXTENSION)) {
return $ext;
}
if ($ext = (MimeTypes::getDefault()->getExtensions($this->contentType)[0] ?? null)) {
return $ext;
}
return null;
}
/**
* Get the array representation of the attachment.
*/
public function toArray(): array
{
return [
'filename' => $this->filename,
'content_type' => $this->contentType,
'contents' => $this->contents(),
];
}
/**
* Get the JSON representation of the attachment.
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}

View File

@@ -0,0 +1,275 @@
<?php
namespace DirectoryTree\ImapEngine;
use Countable;
use DirectoryTree\ImapEngine\Connection\Responses\Data\ListData;
use DirectoryTree\ImapEngine\Connection\Tokens\Nil;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
use Illuminate\Contracts\Support\Arrayable;
use IteratorAggregate;
use JsonSerializable;
use Traversable;
/**
* @implements IteratorAggregate<int, BodyStructurePart|BodyStructureCollection>
*/
class BodyStructureCollection implements Arrayable, Countable, IteratorAggregate, JsonSerializable
{
/**
* Constructor.
*
* @param array<BodyStructurePart|BodyStructureCollection> $parts
*/
public function __construct(
protected string $subtype = 'mixed',
protected array $parameters = [],
protected array $parts = [],
) {}
/**
* Parse a multipart BODYSTRUCTURE ListData into a BodyStructureCollection.
*/
public static function fromListData(ListData $data, ?string $partNumber = null): static
{
$tokens = $data->tokens();
$parts = [];
$childIndex = 1;
$subtypeIndex = null;
foreach ($tokens as $index => $token) {
if ($token instanceof Token && ! $token instanceof Nil) {
$subtypeIndex = $index;
break;
}
if (! $token instanceof ListData) {
continue;
}
$childPartNumber = $partNumber ? "{$partNumber}.{$childIndex}" : (string) $childIndex;
$parts[] = static::isMultipart($token)
? static::fromListData($token, $childPartNumber)
: BodyStructurePart::fromListData($token, $childPartNumber);
$childIndex++;
}
$parameters = [];
if ($subtypeIndex) {
foreach (array_slice($tokens, $subtypeIndex + 1) as $token) {
if ($token instanceof ListData && ! static::isDispositionList($token)) {
$parameters = $token->toKeyValuePairs();
break;
}
}
}
return new static(
$subtypeIndex ? strtolower($tokens[$subtypeIndex]->value) : 'mixed',
$parameters,
$parts
);
}
/**
* Determine if a ListData represents a multipart structure.
*/
protected static function isMultipart(ListData $data): bool
{
return head($data->tokens()) instanceof ListData;
}
/**
* Determine if a ListData represents a disposition (INLINE or ATTACHMENT).
*/
protected static function isDispositionList(ListData $data): bool
{
$tokens = $data->tokens();
if (count($tokens) < 2 || ! isset($tokens[0]) || ! $tokens[0] instanceof Token) {
return false;
}
return in_array(strtoupper($tokens[0]->value), ['INLINE', 'ATTACHMENT']);
}
/**
* Get the multipart subtype (mixed, alternative, related, etc.).
*/
public function subtype(): string
{
return $this->subtype;
}
/**
* Get the content type.
*/
public function contentType(): string
{
return "multipart/{$this->subtype}";
}
/**
* Get the parameters (e.g., boundary).
*/
public function parameters(): array
{
return $this->parameters;
}
/**
* Get the boundary parameter.
*/
public function boundary(): ?string
{
return $this->parameters['boundary'] ?? null;
}
/**
* Get the direct child parts.
*
* @return array<BodyStructurePart|BodyStructureCollection>
*/
public function parts(): array
{
return $this->parts;
}
/**
* Get all parts flattened (including nested parts).
*
* @return BodyStructurePart[]
*/
public function flatten(): array
{
$flattened = [];
foreach ($this->parts as $part) {
if ($part instanceof self) {
$flattened = array_merge($flattened, $part->flatten());
} else {
$flattened[] = $part;
}
}
return $flattened;
}
/**
* Find a part by its part number.
*/
public function find(string $partNumber): BodyStructurePart|BodyStructureCollection|null
{
foreach ($this->parts as $part) {
if ($part instanceof self) {
if ($found = $part->find($partNumber)) {
return $found;
}
} elseif ($part->partNumber() === $partNumber) {
return $part;
}
}
return null;
}
/**
* Get the text/plain part if available.
*/
public function text(): ?BodyStructurePart
{
foreach ($this->flatten() as $part) {
if ($part->isText()) {
return $part;
}
}
return null;
}
/**
* Get the text/html part if available.
*/
public function html(): ?BodyStructurePart
{
foreach ($this->flatten() as $part) {
if ($part->isHtml()) {
return $part;
}
}
return null;
}
/**
* Get all attachment parts.
*
* @return BodyStructurePart[]
*/
public function attachments(): array
{
return array_values(array_filter(
$this->flatten(),
fn (BodyStructurePart $part) => $part->isAttachment()
));
}
/**
* Determine if the collection has attachments.
*/
public function hasAttachments(): bool
{
return count($this->attachments()) > 0;
}
/**
* Get the count of attachments.
*/
public function attachmentCount(): int
{
return count($this->attachments());
}
/**
* Get the count of parts.
*/
public function count(): int
{
return count($this->parts);
}
/**
* Get an iterator for the parts.
*/
public function getIterator(): Traversable
{
yield from $this->parts;
}
/**
* Get the array representation.
*/
public function toArray(): array
{
return [
'subtype' => $this->subtype,
'parameters' => $this->parameters,
'content_type' => $this->contentType(),
'parts' => array_map(fn (Arrayable $part) => $part->toArray(), $this->parts),
];
}
/**
* Get the JSON representation.
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}

View File

@@ -0,0 +1,271 @@
<?php
namespace DirectoryTree\ImapEngine;
use DirectoryTree\ImapEngine\Connection\Responses\Data\ListData;
use DirectoryTree\ImapEngine\Connection\Tokens\Nil;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;
class BodyStructurePart implements Arrayable, JsonSerializable
{
/**
* Constructor.
*/
public function __construct(
protected string $partNumber,
protected string $type,
protected string $subtype,
protected array $parameters = [],
protected ?string $id = null,
protected ?string $description = null,
protected ?string $encoding = null,
protected ?int $size = null,
protected ?int $lines = null,
protected ?ContentDisposition $disposition = null,
) {}
/**
* Parse a single part BODYSTRUCTURE ListData into a BodyStructurePart.
*/
public static function fromListData(ListData $data, string $partNumber = '1'): static
{
return static::parse($data->tokens(), $partNumber);
}
/**
* Parse a single (non-multipart) part.
*
* @param array<Token|ListData> $tokens
*/
protected static function parse(array $tokens, string $partNumber): static
{
return new static(
partNumber: $partNumber,
type: strtolower(static::tokenValueAt($tokens, 0) ?? 'text'),
subtype: strtolower(static::tokenValueAt($tokens, 1) ?? 'plain'),
parameters: isset($tokens[2]) && $tokens[2] instanceof ListData ? $tokens[2]->toKeyValuePairs() : [],
id: static::tokenValueAt($tokens, 3),
description: static::tokenValueAt($tokens, 4),
encoding: static::tokenValueAt($tokens, 5),
size: static::tokenIntValueAt($tokens, 6),
lines: static::tokenIntValueAt($tokens, 7),
disposition: ContentDisposition::parse($tokens),
);
}
/**
* Safely read a scalar token value from the parsed body structure.
*
* @param array<Token|ListData> $tokens
*/
protected static function tokenValueAt(array $tokens, int $index): ?string
{
$token = $tokens[$index] ?? null;
if (! $token instanceof Token || $token instanceof Nil) {
return null;
}
return $token->value;
}
/**
* Safely read an integer token value from the parsed body structure.
*
* @param array<Token|ListData> $tokens
*/
protected static function tokenIntValueAt(array $tokens, int $index): ?int
{
$value = static::tokenValueAt($tokens, $index);
return $value === null ? null : (int) $value;
}
/**
* Get the part number (e.g., "1", "1.2", "2.1.3").
*/
public function partNumber(): string
{
return $this->partNumber;
}
/**
* Get the MIME type (e.g., "text", "image", "multipart").
*/
public function type(): string
{
return $this->type;
}
/**
* Get the MIME subtype (e.g., "plain", "html", "jpeg", "mixed").
*/
public function subtype(): string
{
return $this->subtype;
}
/**
* Get the full content type (e.g., "text/plain", "multipart/alternative").
*/
public function contentType(): string
{
return "{$this->type}/{$this->subtype}";
}
/**
* Get the parameters (e.g., charset, boundary).
*/
public function parameters(): array
{
return $this->parameters;
}
/**
* Get a specific parameter value.
*/
public function parameter(string $name): ?string
{
return $this->parameters[strtolower($name)] ?? null;
}
/**
* Get the content ID.
*/
public function id(): ?string
{
return $this->id;
}
/**
* Get the content description.
*/
public function description(): ?string
{
return $this->description;
}
/**
* Get the content transfer encoding.
*/
public function encoding(): ?string
{
return $this->encoding;
}
/**
* Get the size in bytes.
*/
public function size(): ?int
{
return $this->size;
}
/**
* Get the number of lines (for text parts).
*/
public function lines(): ?int
{
return $this->lines;
}
/**
* Get the content disposition.
*/
public function disposition(): ?ContentDisposition
{
return $this->disposition;
}
/**
* Get the filename from disposition parameters.
*/
public function filename(): ?string
{
return $this->disposition?->filename() ?? $this->parameters['name'] ?? null;
}
/**
* Get the charset from parameters.
*/
public function charset(): ?string
{
return $this->parameters['charset'] ?? null;
}
/**
* Determine if this is a text part.
*/
public function isText(): bool
{
return $this->type === 'text' && $this->subtype === 'plain';
}
/**
* Determine if this is an HTML part.
*/
public function isHtml(): bool
{
return $this->type === 'text' && $this->subtype === 'html';
}
/**
* Determine if this is an attachment.
*/
public function isAttachment(): bool
{
if ($this->disposition?->isAttachment()) {
return true;
}
// Inline parts are not attachments.
if ($this->disposition?->isInline()) {
return false;
}
// Consider non-text/html parts with filenames as attachments.
if ($this->filename() && ! $this->isText() && ! $this->isHtml()) {
return true;
}
return false;
}
/**
* Determine if this is an inline part.
*/
public function isInline(): bool
{
return $this->disposition?->isInline() ?? false;
}
/**
* Get the array representation.
*/
public function toArray(): array
{
return [
'id' => $this->id,
'type' => $this->type,
'size' => $this->size,
'lines' => $this->lines,
'subtype' => $this->subtype,
'encoding' => $this->encoding,
'parameters' => $this->parameters,
'part_number' => $this->partNumber,
'description' => $this->description,
'content_type' => $this->contentType(),
'disposition' => $this->disposition?->toArray(),
];
}
/**
* Get the JSON representation.
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace DirectoryTree\ImapEngine\Collections;
use DirectoryTree\ImapEngine\FolderInterface;
use Illuminate\Support\Collection;
/**
* @template-extends Collection<array-key, FolderInterface>
*/
class FolderCollection extends Collection {}

View File

@@ -0,0 +1,32 @@
<?php
namespace DirectoryTree\ImapEngine\Collections;
use DirectoryTree\ImapEngine\Message;
use DirectoryTree\ImapEngine\MessageInterface;
/**
* @template-extends PaginatedCollection<array-key, MessageInterface|Message>
*/
class MessageCollection extends PaginatedCollection
{
/**
* Find a message by its UID.
*/
public function find(int $uid): ?MessageInterface
{
return $this->first(
fn (MessageInterface $message) => $message->uid() === $uid
);
}
/**
* Find a message by its UID or throw an exception.
*/
public function findOrFail(int $uid): MessageInterface
{
return $this->firstOrFail(
fn (MessageInterface $message) => $message->uid() === $uid
);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace DirectoryTree\ImapEngine\Collections;
use DirectoryTree\ImapEngine\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
/**
* @template TKey of array-key
* @template TValue
*
* @template-extends Collection<TKey, TValue>
*/
class PaginatedCollection extends Collection
{
/**
* The total number of items.
*/
protected int $total = 0;
/**
* Paginate the current collection.
*
* @return LengthAwarePaginator<TKey, TValue>
*/
public function paginate(int $perPage = 15, ?int $page = null, string $pageName = 'page', bool $prepaginated = false): LengthAwarePaginator
{
$total = $this->total ?: $this->count();
$results = ! $prepaginated && $total ? $this->forPage($page, $perPage) : $this;
return $this->paginator($results, $total, $perPage, $page, $pageName);
}
/**
* Create a new length-aware paginator instance.
*
* @return LengthAwarePaginator<TKey, TValue>
*/
protected function paginator(Collection $items, int $total, int $perPage, ?int $currentPage, string $pageName): LengthAwarePaginator
{
return new LengthAwarePaginator($items, $total, $perPage, $currentPage, $pageName);
}
/**
* Get or set the total amount.
*/
public function total(?int $total = null): ?int
{
if (is_null($total)) {
return $this->total;
}
return $this->total = $total;
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace DirectoryTree\ImapEngine\Collections;
use DirectoryTree\ImapEngine\Connection\Responses\ContinuationResponse;
use DirectoryTree\ImapEngine\Connection\Responses\TaggedResponse;
use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse;
use Illuminate\Support\Collection;
/**
* @template TKey of array-key
*
* @template-covariant TValue
*
* @extends Collection<array-key, TValue>
*/
class ResponseCollection extends Collection
{
/**
* Filter the collection to only tagged responses.
*
* @return self<array-key, TaggedResponse>
*/
public function tagged(): self
{
return $this->whereInstanceOf(TaggedResponse::class);
}
/**
* Filter the collection to only untagged responses.
*
* @return self<array-key, UntaggedResponse>
*/
public function untagged(): self
{
return $this->whereInstanceOf(UntaggedResponse::class);
}
/**
* Filter the collection to only continuation responses.
*
* @return self<array-key, ContinuationResponse>
*/
public function continuation(): self
{
return $this->whereInstanceOf(ContinuationResponse::class);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace DirectoryTree\ImapEngine;
trait ComparesFolders
{
/**
* Determine if two folders are the same.
*/
protected function isSameFolder(FolderInterface $a, FolderInterface $b): bool
{
return $a->path() === $b->path()
&& $a->mailbox()->config('host') === $b->mailbox()->config('host')
&& $a->mailbox()->config('username') === $b->mailbox()->config('username');
}
}

View File

@@ -0,0 +1,344 @@
<?php
namespace DirectoryTree\ImapEngine\Connection;
use DirectoryTree\ImapEngine\Collections\ResponseCollection;
use DirectoryTree\ImapEngine\Connection\Responses\TaggedResponse;
use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse;
use DirectoryTree\ImapEngine\Enums\ImapFetchIdentifier;
use DirectoryTree\ImapEngine\Enums\ImapSortKey;
use Generator;
interface ConnectionInterface
{
/**
* Open a new connection.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-state-and-flow-diagram
*/
public function connect(string $host, ?int $port = null, array $options = []): void;
/**
* Close the current connection.
*/
public function disconnect(): void;
/**
* Determine if the current session is connected.
*/
public function connected(): bool;
/**
* Send a "LOGIN" command.
*
* Login to a new session.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-login-command
*/
public function login(string $user, string $password): TaggedResponse;
/**
* Send a "LOGOUT" command.
*
* Logout of the current server session.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-logout-command
*/
public function logout(): void;
/**
* Send an "AUTHENTICATE" command.
*
* Authenticate the current session.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-authenticate-command
*/
public function authenticate(string $user, string $token): TaggedResponse;
/**
* Send a "STARTTLS" command.
*
* Upgrade the current plaintext connection to a secure TLS-encrypted connection.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-starttls-command
*/
public function startTls(): void;
/**
* Send an "IDLE" command.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-idle-command
*/
public function idle(int $timeout): Generator;
/**
* Send a "DONE" command.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#section-6.3.13
*/
public function done(): void;
/**
* Send a "NOOP" command.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-noop-command
*/
public function noop(): TaggedResponse;
/**
* Send a "EXPUNGE" command.
*
* Apply session saved changes to the server.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-expunge-command
*/
public function expunge(): ResponseCollection;
/**
* Send a "CAPABILITY" command.
*
* Get the mailbox's available capabilities.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-capability-command
*/
public function capability(): UntaggedResponse;
/**
* Send a "SEARCH" command.
*
* Execute a search request.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-search-command
*/
public function search(array $params): UntaggedResponse;
/**
* Send a "SORT" command.
*
* Execute a sort request using RFC 5256.
*
* @see https://datatracker.ietf.org/doc/html/rfc5256
*/
public function sort(ImapSortKey $key, string $direction, array $params): UntaggedResponse;
/**
* Send a "FETCH" command.
*
* Exchange identification information.
*
* @see https://datatracker.ietf.org/doc/html/rfc2971.
*/
public function id(?array $ids = null): UntaggedResponse;
/**
* Send a "FETCH UID" command.
*
* Fetch message UIDs using the given message numbers.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-uid-command
*/
public function uid(int|array $ids, ImapFetchIdentifier $identifier): ResponseCollection;
/**
* Send a "FETCH BODY[TEXT]" command.
*
* Fetch message text contents.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#section-6.4.5-9.9
*/
public function bodyText(int|array $ids, bool $peek = true): ResponseCollection;
/**
* Send a "FETCH BODY[HEADER]" command.
*
* Fetch message headers.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#section-6.4.5-9.9
*/
public function bodyHeader(int|array $ids, bool $peek = true): ResponseCollection;
/**
* Send a "FETCH BODYSTRUCTURE" command.
*
* Fetch message body structure.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#section-6.4.5-9.9
*/
public function bodyStructure(int|array $ids): ResponseCollection;
/**
* Send a "FETCH BODY[i]" command.
*
* Fetch a specific part of the message BODY, such as BODY[1], BODY[1.2], etc.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#section-6.4.5-9.9
*/
public function bodyPart(string $partIndex, int|array $ids, bool $peek = false): ResponseCollection;
/**
* Send a "FETCH FLAGS" command.
*
* Fetch a message flags.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#section-6.4.5-9.17
*/
public function flags(int|array $ids): ResponseCollection;
/**
* Send a "FETCH" command.
*
* Fetch one or more items for one or more messages.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-fetch-command
*/
public function fetch(array|string $items, array|int $from, mixed $to = null, ImapFetchIdentifier $identifier = ImapFetchIdentifier::Uid): ResponseCollection;
/**
* Send a "RFC822.SIZE" command.
*
* Fetch message sizes for one or more messages.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#section-6.4.5-9.21
*/
public function size(int|array $ids): ResponseCollection;
/**
* Send an IMAP command.
*/
public function send(string $name, array $tokens = [], ?string &$tag = null): void;
/**
* Send a "SELECT" command.
*
* Select the specified folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-select-command
*/
public function select(string $folder): ResponseCollection;
/**
* Send a "EXAMINE" command.
*
* Examine a given folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-examine-command
*/
public function examine(string $folder): ResponseCollection;
/**
* Send a "LIST" command.
*
* Get a list of available folders.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-list-command
*/
public function list(string $reference = '', string $folder = '*'): ResponseCollection;
/**
* Send a "STATUS" command.
*
* Get the status of a given folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-status-command
*/
public function status(string $folder, array $arguments = ['MESSAGES', 'UNSEEN', 'RECENT', 'UIDNEXT', 'UIDVALIDITY']): UntaggedResponse;
/**
* Send a "STORE" command.
*
* Set message flags.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-store-command
*/
public function store(array|string $flags, array|int $from, ?int $to = null, ?string $mode = null, bool $silent = true, ?string $item = null): ResponseCollection;
/**
* Send a "APPEND" command.
*
* Append a new message to given folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-append-command
*/
public function append(string $folder, string $message, ?array $flags = null): TaggedResponse;
/**
* Send a "UID COPY" command.
*
* Copy message set from current folder to other folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-copy-command
*/
public function copy(string $folder, array|int $from, ?int $to = null): TaggedResponse;
/**
* Send a "UID MOVE" command.
*
* Move a message set from current folder to another folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-move-command
*/
public function move(string $folder, array|int $from, ?int $to = null): TaggedResponse;
/**
* Send a "CREATE" command.
*
* Create a new folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-create-command
*/
public function create(string $folder): ResponseCollection;
/**
* Send a "DELETE" command.
*
* Delete a folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-delete-command
*/
public function delete(string $folder): TaggedResponse;
/**
* Send a "RENAME" command.
*
* Rename an existing folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-rename-command
*/
public function rename(string $oldPath, string $newPath): TaggedResponse;
/**
* Send a "SUBSCRIBE" command.
*
* Subscribe to a folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-subscribe-command
*/
public function subscribe(string $folder): TaggedResponse;
/**
* Send a "UNSUBSCRIBE" command.
*
* Unsubscribe from a folder.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-unsubscribe-command
*/
public function unsubscribe(string $folder): TaggedResponse;
/**
* Send a "GETQUOTA" command.
*
* Retrieve quota information about a specific quota root.
*
* @see https://datatracker.ietf.org/doc/html/rfc9208#name-getquota
*/
public function quota(string $root): UntaggedResponse;
/**
* Send a "GETQUOTAROOT" command.
*
* Retrieve quota root information about a mailbox.
*
* @see https://datatracker.ietf.org/doc/html/rfc9208#name-getquotaroot
*/
public function quotaRoot(string $mailbox): ResponseCollection;
}

View File

@@ -0,0 +1,105 @@
<?php
namespace DirectoryTree\ImapEngine\Connection;
use Stringable;
class ImapCommand implements Stringable
{
/**
* The compiled command lines.
*
* @var string[]
*/
protected ?array $compiled = null;
/**
* Constructor.
*/
public function __construct(
protected string $tag,
protected string $command,
protected array $tokens = [],
) {}
/**
* Get the IMAP tag.
*/
public function tag(): string
{
return $this->tag;
}
/**
* Get the IMAP command.
*/
public function command(): string
{
return $this->command;
}
/**
* Get the IMAP tokens.
*/
public function tokens(): array
{
return $this->tokens;
}
/**
* Compile the command into lines for transmission.
*
* @return string[]
*/
public function compile(): array
{
if (is_array($this->compiled)) {
return $this->compiled;
}
$lines = [];
$line = trim("{$this->tag} {$this->command}");
foreach ($this->tokens as $token) {
if (is_array($token)) {
// For tokens provided as arrays, the first element is a placeholder
// (for example, "{20}") that signals a literal value will follow.
// The second element holds the actual literal content.
[$placeholder, $literal] = $token;
$lines[] = "{$line} {$placeholder}";
$line = $literal;
} else {
$line .= " {$token}";
}
}
$lines[] = $line;
return $this->compiled = $lines;
}
/**
* Get a redacted version of the command for safe exposure.
*/
public function redacted(): ImapCommand
{
return new static($this->tag, $this->command, array_map(
function (mixed $token) {
return is_array($token)
? array_map(fn () => '[redacted]', $token)
: '[redacted]';
}, $this->tokens)
);
}
/**
* Get the command as a string.
*/
public function __toString(): string
{
return implode("\r\n", $this->compile());
}
}

View File

@@ -0,0 +1,815 @@
<?php
namespace DirectoryTree\ImapEngine\Connection;
use DirectoryTree\ImapEngine\Collections\ResponseCollection;
use DirectoryTree\ImapEngine\Connection\Loggers\LoggerInterface;
use DirectoryTree\ImapEngine\Connection\Responses\ContinuationResponse;
use DirectoryTree\ImapEngine\Connection\Responses\Data\Data;
use DirectoryTree\ImapEngine\Connection\Responses\Data\ListData;
use DirectoryTree\ImapEngine\Connection\Responses\Response;
use DirectoryTree\ImapEngine\Connection\Responses\TaggedResponse;
use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse;
use DirectoryTree\ImapEngine\Connection\Streams\FakeStream;
use DirectoryTree\ImapEngine\Connection\Streams\StreamInterface;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
use DirectoryTree\ImapEngine\Enums\ImapFetchIdentifier;
use DirectoryTree\ImapEngine\Enums\ImapSortKey;
use DirectoryTree\ImapEngine\Exceptions\ImapCommandException;
use DirectoryTree\ImapEngine\Exceptions\ImapConnectionClosedException;
use DirectoryTree\ImapEngine\Exceptions\ImapConnectionFailedException;
use DirectoryTree\ImapEngine\Exceptions\ImapConnectionTimedOutException;
use DirectoryTree\ImapEngine\Exceptions\ImapResponseException;
use DirectoryTree\ImapEngine\Exceptions\ImapStreamException;
use DirectoryTree\ImapEngine\Support\Str;
use Exception;
use Generator;
use LogicException;
use Throwable;
class ImapConnection implements ConnectionInterface
{
/**
* Sequence number used to generate unique command tags.
*/
protected int $sequence = 0;
/**
* The result instance.
*/
protected ?Result $result = null;
/**
* The parser instance.
*/
protected ?ImapParser $parser = null;
/**
* Constructor.
*/
public function __construct(
protected StreamInterface $stream,
protected ?LoggerInterface $logger = null,
) {}
/**
* Create a new connection with a fake stream.
*/
public static function fake(array $responses = []): static
{
$stream = new FakeStream;
$stream->open();
$stream->feed($responses);
return new static($stream);
}
/**
* Tear down the connection.
*/
public function __destruct()
{
if (! $this->connected()) {
return;
}
try {
@$this->logout();
} catch (Exception $e) {
// Do nothing.
}
}
/**
* {@inheritDoc}
*/
public function connect(string $host, ?int $port = null, array $options = []): void
{
$transport = strtolower($options['encryption'] ?? '') ?: 'tcp';
if (in_array($transport, ['ssl', 'tls'])) {
$port ??= 993;
} else {
$port ??= 143;
}
$this->setParser(
$this->newParser($this->stream)
);
$this->stream->open(
$transport === 'starttls' ? 'tcp' : $transport,
$host,
$port,
$options['timeout'] ?? 30,
$this->getDefaultSocketOptions(
$transport,
$options['proxy'] ?? [],
$options['validate_cert'] ?? true
)
);
$this->assertNextResponse(
fn (Response $response) => $response instanceof UntaggedResponse,
fn (UntaggedResponse $response) => $response->type()->is('OK'),
fn () => new ImapConnectionFailedException("Connection to $host:$port failed")
);
if ($transport === 'starttls') {
$this->startTls();
}
}
/**
* Get the default socket options for the given transport.
*
* @param 'ssl'|'tls'|'starttls'|'tcp' $transport
*/
protected function getDefaultSocketOptions(string $transport, array $proxy = [], bool $validateCert = true): array
{
$options = [];
$key = match ($transport) {
'ssl', 'tls' => 'ssl',
'starttls', 'tcp' => 'tcp',
};
if (in_array($transport, ['ssl', 'tls'])) {
$options[$key] = [
'verify_peer' => $validateCert,
'verify_peer_name' => $validateCert,
];
}
if (! isset($proxy['socket'])) {
return $options;
}
$options[$key]['proxy'] = $proxy['socket'];
$options[$key]['request_fulluri'] = $proxy['request_fulluri'] ?? false;
if (isset($proxy['username'])) {
$auth = base64_encode($proxy['username'].':'.$proxy['password']);
$options[$key]['header'] = ["Proxy-Authorization: Basic $auth"];
}
return $options;
}
/**
* {@inheritDoc}
*/
public function disconnect(): void
{
$this->stream->close();
}
/**
* {@inheritDoc}
*/
public function connected(): bool
{
return $this->stream->opened();
}
/**
* {@inheritDoc}
*/
public function login(string $user, string $password): TaggedResponse
{
$this->send('LOGIN', Str::literal([$user, $password]), $tag);
return $this->assertTaggedResponse($tag, fn (TaggedResponse $response) => (
ImapCommandException::make($this->result->command()->redacted(), $response)
));
}
/**
* {@inheritDoc}
*/
public function logout(): void
{
$this->send('LOGOUT', tag: $tag);
}
/**
* {@inheritDoc}
*/
public function authenticate(string $user, string $token): TaggedResponse
{
$this->send('AUTHENTICATE', ['XOAUTH2', Str::credentials($user, $token)], $tag);
return $this->assertTaggedResponse($tag, fn (TaggedResponse $response) => (
ImapCommandException::make($this->result->command()->redacted(), $response)
));
}
/**
* {@inheritDoc}
*/
public function startTls(): void
{
$this->send('STARTTLS', tag: $tag);
$this->assertTaggedResponse($tag);
$this->stream->setSocketSetCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
}
/**
* {@inheritDoc}
*/
public function select(string $folder = 'INBOX'): ResponseCollection
{
return $this->examineOrSelect('SELECT', $folder);
}
/**
* {@inheritDoc}
*/
public function examine(string $folder = 'INBOX'): ResponseCollection
{
return $this->examineOrSelect('EXAMINE', $folder);
}
/**
* Examine and select have the same response.
*/
protected function examineOrSelect(string $command = 'EXAMINE', string $folder = 'INBOX'): ResponseCollection
{
$this->send($command, [Str::literal($folder)], $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged();
}
/**
* {@inheritDoc}
*/
public function status(string $folder = 'INBOX', array $arguments = ['MESSAGES', 'UNSEEN', 'RECENT', 'UIDNEXT', 'UIDVALIDITY']): UntaggedResponse
{
$this->send('STATUS', [
Str::literal($folder),
Str::list($arguments),
], $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->firstWhere(
fn (UntaggedResponse $response) => $response->type()->is('STATUS')
);
}
/**
* {@inheritDoc}
*/
public function create(string $folder): ResponseCollection
{
$this->send('CREATE', [Str::literal($folder)], $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->filter(
fn (UntaggedResponse $response) => $response->type()->is('LIST')
);
}
/**
* {@inheritDoc}
*/
public function delete(string $folder): TaggedResponse
{
$this->send('DELETE', [Str::literal($folder)], tag: $tag);
return $this->assertTaggedResponse($tag);
}
/**
* {@inheritDoc}
*/
public function rename(string $oldPath, string $newPath): TaggedResponse
{
$this->send('RENAME', Str::literal([$oldPath, $newPath]), tag: $tag);
return $this->assertTaggedResponse($tag);
}
/**
* {@inheritDoc}
*/
public function subscribe(string $folder): TaggedResponse
{
$this->send('SUBSCRIBE', [Str::literal($folder)], tag: $tag);
return $this->assertTaggedResponse($tag);
}
/**
* {@inheritDoc}
*/
public function unsubscribe(string $folder): TaggedResponse
{
$this->send('UNSUBSCRIBE', [Str::literal($folder)], tag: $tag);
return $this->assertTaggedResponse($tag);
}
/**
* {@inheritDoc}
*/
public function quota(string $root): UntaggedResponse
{
$this->send('GETQUOTA', [Str::literal($root)], tag: $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->firstOrFail(
fn (UntaggedResponse $response) => $response->type()->is('QUOTA')
);
}
/**
* {@inheritDoc}
*/
public function quotaRoot(string $mailbox): ResponseCollection
{
$this->send('GETQUOTAROOT', [Str::literal($mailbox)], tag: $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->filter(
fn (UntaggedResponse $response) => $response->type()->is('QUOTA')
);
}
/**
* {@inheritDoc}
*/
public function list(string $reference = '', string $folder = '*'): ResponseCollection
{
$this->send('LIST', Str::literal([$reference, $folder]), $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->filter(
fn (UntaggedResponse $response) => $response->type()->is('LIST')
);
}
/**
* {@inheritDoc}
*/
public function append(string $folder, string $message, ?array $flags = null): TaggedResponse
{
$tokens = [];
$tokens[] = Str::literal($folder);
if ($flags) {
$tokens[] = Str::list($flags);
}
$tokens[] = Str::literal($message);
$this->send('APPEND', $tokens, tag: $tag);
return $this->assertTaggedResponse($tag);
}
/**
* {@inheritDoc}
*/
public function copy(string $folder, array|int $from, ?int $to = null): TaggedResponse
{
$this->send('UID COPY', [
Str::set($from, $to),
Str::literal($folder),
], $tag);
return $this->assertTaggedResponse($tag);
}
/**
* {@inheritDoc}
*/
public function move(string $folder, array|int $from, ?int $to = null): TaggedResponse
{
$this->send('UID MOVE', [
Str::set($from, $to),
Str::literal($folder),
], $tag);
return $this->assertTaggedResponse($tag);
}
/**
* {@inheritDoc}
*/
public function store(array|string $flags, array|int $from, ?int $to = null, ?string $mode = null, bool $silent = true, ?string $item = null): ResponseCollection
{
$set = Str::set($from, $to);
$flags = Str::list((array) $flags);
$item = ($mode == '-' ? '-' : '+').(is_null($item) ? 'FLAGS' : $item).($silent ? '.SILENT' : '');
$this->send('UID STORE', [$set, $item, $flags], tag: $tag);
$this->assertTaggedResponse($tag);
return $silent ? new ResponseCollection : $this->result->responses()->untagged()->filter(
fn (UntaggedResponse $response) => $response->type()->is('FETCH')
);
}
/**
* {@inheritDoc}
*/
public function uid(int|array $ids, ImapFetchIdentifier $identifier): ResponseCollection
{
return $this->fetch(['UID'], (array) $ids, null, $identifier);
}
/**
* {@inheritDoc}
*/
public function bodyText(int|array $ids, bool $peek = true): ResponseCollection
{
return $this->fetch([$peek ? 'BODY.PEEK[TEXT]' : 'BODY[TEXT]'], (array) $ids);
}
/**
* {@inheritDoc}
*/
public function bodyHeader(int|array $ids, bool $peek = true): ResponseCollection
{
return $this->fetch([$peek ? 'BODY.PEEK[HEADER]' : 'BODY[HEADER]'], (array) $ids);
}
/**
* Fetch the BODYSTRUCTURE for the given message(s).
*/
public function bodyStructure(int|array $ids): ResponseCollection
{
return $this->fetch(['BODYSTRUCTURE'], (array) $ids);
}
/**
* Fetch a specific part of the message BODY, such as BODY[1], BODY[1.2], etc.
*/
public function bodyPart(string $partIndex, int|array $ids, bool $peek = false): ResponseCollection
{
$part = $peek ? "BODY.PEEK[$partIndex]" : "BODY[$partIndex]";
return $this->fetch([$part], (array) $ids);
}
/**
* {@inheritDoc}
*/
public function flags(int|array $ids): ResponseCollection
{
return $this->fetch(['FLAGS'], (array) $ids);
}
/**
* {@inheritDoc}
*/
public function size(int|array $ids): ResponseCollection
{
return $this->fetch(['RFC822.SIZE'], (array) $ids);
}
/**
* {@inheritDoc}
*/
public function search(array $params): UntaggedResponse
{
$this->send('UID SEARCH', $params, tag: $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->firstOrFail(
fn (UntaggedResponse $response) => $response->type()->is('SEARCH')
);
}
/**
* {@inheritDoc}
*/
public function sort(ImapSortKey $key, string $direction, array $params): UntaggedResponse
{
$sortCriteria = $direction === 'desc' ? "REVERSE {$key->value}" : $key->value;
$this->send('UID SORT', ["({$sortCriteria})", 'UTF-8', ...$params], tag: $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->firstOrFail(
fn (UntaggedResponse $response) => $response->type()->is('SORT')
);
}
/**
* {@inheritDoc}
*/
public function capability(): UntaggedResponse
{
$this->send('CAPABILITY', tag: $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->firstOrFail(
fn (UntaggedResponse $response) => $response->type()->is('CAPABILITY')
);
}
/**
* {@inheritDoc}
*/
public function id(?array $ids = null): UntaggedResponse
{
$token = 'NIL';
if (is_array($ids) && ! empty($ids)) {
$token = '(';
foreach ($ids as $id) {
$token .= '"'.Str::escape($id).'" ';
}
$token = rtrim($token).')';
}
$this->send('ID', [$token], tag: $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged()->firstOrFail(
fn (UntaggedResponse $response) => $response->type()->is('ID')
);
}
/**
* {@inheritDoc}
*/
public function expunge(): ResponseCollection
{
$this->send('EXPUNGE', tag: $tag);
$this->assertTaggedResponse($tag);
return $this->result->responses()->untagged();
}
/**
* {@inheritDoc}
*/
public function noop(): TaggedResponse
{
$this->send('NOOP', tag: $tag);
return $this->assertTaggedResponse($tag);
}
/**
* {@inheritDoc}
*/
public function idle(int $timeout): Generator
{
$this->stream->setTimeout($timeout);
$this->send('IDLE', tag: $tag);
$this->assertNextResponse(
fn (Response $response) => $response instanceof ContinuationResponse,
fn (ContinuationResponse $response) => true,
fn (ContinuationResponse $response) => ImapCommandException::make(new ImapCommand('', 'IDLE'), $response),
);
while ($response = $this->nextReply()) {
yield $response;
}
}
/**
* {@inheritDoc}
*/
public function done(): void
{
$this->write('DONE');
// After issuing a "DONE" command, the server must eventually respond with a
// tagged response to indicate that the IDLE command has been successfully
// terminated and the server is ready to accept further commands.
$this->assertNextResponse(
fn (Response $response) => $response instanceof TaggedResponse,
fn (TaggedResponse $response) => $response->successful(),
fn (TaggedResponse $response) => ImapCommandException::make(new ImapCommand('', 'DONE'), $response),
);
}
/**
* Send an IMAP command.
*
* @param-out string $tag
*/
public function send(string $name, array $tokens = [], ?string &$tag = null): void
{
if (! $tag) {
$this->sequence++;
$tag = 'TAG'.$this->sequence;
}
$command = new ImapCommand($tag, $name, $tokens);
// After every command, we'll overwrite any previous result
// with the new command and its responses, so that we can
// easily access the commands responses for assertion.
$this->setResult(new Result($command));
foreach ($command->compile() as $line) {
$this->write($line);
}
}
/**
* Write data to the connected stream.
*/
protected function write(string $data): void
{
if ($this->stream->fwrite($data."\r\n") === false) {
throw new ImapStreamException('Failed to write data to stream');
}
$this->logger?->sent($data);
}
/**
* Fetch one or more items for one or more messages.
*/
public function fetch(array|string $items, array|int $from, mixed $to = null, ImapFetchIdentifier $identifier = ImapFetchIdentifier::Uid): ResponseCollection
{
$prefix = ($identifier === ImapFetchIdentifier::Uid) ? 'UID' : '';
$this->send(trim($prefix.' FETCH'), [
Str::set($from, $to),
Str::list((array) $items),
], $tag);
$this->assertTaggedResponse($tag);
// Some IMAP servers can send unsolicited untagged responses along with fetch
// requests. We'll need to filter these out so that we can return only the
// responses that are relevant to the fetch command. For example:
// >> TAG123 FETCH (UID 456 BODY[TEXT])
// << * 123 FETCH (UID 456 BODY[TEXT] {14}\nHello, World!)
// << * 123 FETCH (FLAGS (\Seen)) <-- Unsolicited response
return $this->result->responses()->untagged()->filter(function (UntaggedResponse $response) use ($items, $identifier) {
// Skip over any untagged responses that are not FETCH responses.
// The third token should always be the list of data items.
if (! ($data = $response->tokenAt(3)) instanceof ListData) {
return false;
}
return match ($identifier) {
// If we're fetching UIDs, we can check if a UID token is contained in the list.
ImapFetchIdentifier::Uid => $data->contains('UID'),
// If we're fetching message numbers, we can check if the requested items are all contained in the list.
ImapFetchIdentifier::MessageNumber => $data->contains($items),
};
});
}
/**
* Set the current result instance.
*/
protected function setResult(Result $result): void
{
$this->result = $result;
}
/**
* Set the current parser instance.
*/
protected function setParser(ImapParser $parser): void
{
$this->parser = $parser;
}
/**
* Create a new parser instance.
*/
protected function newParser(StreamInterface $stream): ImapParser
{
return new ImapParser($this->newTokenizer($stream));
}
/**
* Create a new tokenizer instance.
*/
protected function newTokenizer(StreamInterface $stream): ImapTokenizer
{
return new ImapTokenizer($stream);
}
/**
* Assert the next response is a successful tagged response.
*/
protected function assertTaggedResponse(string $tag, ?callable $exception = null): TaggedResponse
{
/** @var TaggedResponse $response */
$response = $this->assertNextResponse(
fn (Response $response) => (
$response instanceof TaggedResponse && $response->tag()->is($tag)
),
fn (TaggedResponse $response) => (
$response->successful()
),
$exception ?? fn (TaggedResponse $response) => (
ImapCommandException::make($this->result->command(), $response)
),
);
return $response;
}
/**
* Assert the next response matches the given filter and assertion.
*
* @template T of Response
*
* @param callable(Response): bool $filter
* @param callable(T): bool $assertion
* @param callable(T): Throwable $exception
* @return T
*
* @throws ImapResponseException
*/
protected function assertNextResponse(callable $filter, callable $assertion, callable $exception): Response
{
while ($response = $this->nextResponse($filter)) {
if ($assertion($response)) {
return $response;
}
throw $exception($response);
}
throw new ImapResponseException('No matching response found');
}
/**
* Returns the next response matching the given filter.
*
* @template T of Response
*
* @param callable(T): bool $filter
* @return T|null
*/
protected function nextResponse(callable $filter): ?Response
{
if (! $this->parser) {
throw new LogicException('No parser instance set');
}
while ($response = $this->nextReply()) {
if (! $response instanceof Response) {
continue;
}
$this->result?->addResponse($response);
if ($filter($response)) {
return $response;
}
}
return null;
}
/**
* Read the next reply from the stream.
*/
protected function nextReply(): Data|Token|Response|null
{
if (! $reply = $this->parser->next()) {
$meta = $this->stream->meta();
throw match (true) {
$meta['timed_out'] ?? false => new ImapConnectionTimedOutException('Stream timed out, no response'),
$meta['eof'] ?? false => new ImapConnectionClosedException('Server closed the connection (EOF)'),
default => new ImapConnectionFailedException('Unknown stream error. Metadata: '.json_encode($meta)),
};
}
$this->logger?->received($reply);
return $reply;
}
}

View File

@@ -0,0 +1,270 @@
<?php
namespace DirectoryTree\ImapEngine\Connection;
use DirectoryTree\ImapEngine\Connection\Responses\ContinuationResponse;
use DirectoryTree\ImapEngine\Connection\Responses\Data\Data;
use DirectoryTree\ImapEngine\Connection\Responses\Data\ListData;
use DirectoryTree\ImapEngine\Connection\Responses\Data\ResponseCodeData;
use DirectoryTree\ImapEngine\Connection\Responses\Response;
use DirectoryTree\ImapEngine\Connection\Responses\TaggedResponse;
use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse;
use DirectoryTree\ImapEngine\Connection\Tokens\Atom;
use DirectoryTree\ImapEngine\Connection\Tokens\Crlf;
use DirectoryTree\ImapEngine\Connection\Tokens\ListClose;
use DirectoryTree\ImapEngine\Connection\Tokens\ListOpen;
use DirectoryTree\ImapEngine\Connection\Tokens\Number;
use DirectoryTree\ImapEngine\Connection\Tokens\ResponseCodeClose;
use DirectoryTree\ImapEngine\Connection\Tokens\ResponseCodeOpen;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
use DirectoryTree\ImapEngine\Exceptions\ImapParserException;
class ImapParser
{
/**
* The current token being parsed.
*
* Expected to be an associative array with keys like "type" and "value".
*/
protected ?Token $currentToken = null;
/**
* Constructor.
*/
public function __construct(
protected ImapTokenizer $tokenizer
) {}
/**
* Get the next response from the tokenizer.
*/
public function next(): Data|Token|Response|null
{
// Attempt to load the first token.
if (! $this->currentToken) {
$this->advance();
}
// No token was found, return null.
if (! $this->currentToken) {
return null;
}
// If the token indicates the beginning of a list, parse it.
if ($this->currentToken instanceof ListOpen) {
return $this->parseList();
}
// If the token is an Atom or Number, check its value for special markers.
if ($this->currentToken instanceof Atom || $this->currentToken instanceof Number) {
// '*' marks an untagged response.
if ($this->currentToken->value === '*') {
return $this->parseUntaggedResponse();
}
// '+' marks a continuation response.
if ($this->currentToken->value === '+') {
return $this->parseContinuationResponse();
}
// If it's an ATOM and not '*' or '+', it's likely a tagged response.
return $this->parseTaggedResponse();
}
return $this->parseElement();
}
/**
* Parse an untagged response.
*
* An untagged response begins with the '*' token. It may contain
* multiple elements, including lists and response codes.
*/
protected function parseUntaggedResponse(): UntaggedResponse
{
// Capture the initial '*' token.
$elements[] = clone $this->currentToken;
$this->advance();
// Collect all tokens until the end-of-response marker.
while ($this->currentToken && ! $this->currentToken instanceof Crlf) {
$elements[] = $this->parseElement();
}
// If the end-of-response marker (CRLF) is present, consume it.
if ($this->currentToken && $this->currentToken instanceof Crlf) {
$this->currentToken = null;
} else {
throw new ImapParserException('Unterminated untagged response');
}
return new UntaggedResponse($elements);
}
/**
* Parse a continuation response.
*
* A continuation response starts with a '+' token, indicating
* that the server expects additional data from the client.
*/
protected function parseContinuationResponse(): ContinuationResponse
{
// Capture the initial '+' token.
$elements[] = clone $this->currentToken;
$this->advance();
// Collect all tokens until the CRLF marker.
while ($this->currentToken && ! $this->currentToken instanceof Crlf) {
$elements[] = $this->parseElement();
}
// Consume the CRLF marker if present.
if ($this->currentToken && $this->currentToken instanceof Crlf) {
$this->currentToken = null;
} else {
throw new ImapParserException('Unterminated continuation response');
}
return new ContinuationResponse($elements);
}
/**
* Parse a tagged response.
*
* A tagged response begins with a tag (which is not '*' or '+')
* and is followed by a status and optional data.
*/
protected function parseTaggedResponse(): TaggedResponse
{
// Capture the initial TAG token.
$tokens[] = clone $this->currentToken;
$this->advance();
// Collect tokens until the end-of-response marker is reached.
while ($this->currentToken && ! $this->currentToken instanceof Crlf) {
$tokens[] = $this->parseElement();
}
// Consume the CRLF marker if present.
if ($this->currentToken && $this->currentToken instanceof Crlf) {
$this->currentToken = null;
} else {
throw new ImapParserException('Unterminated tagged response');
}
return new TaggedResponse($tokens);
}
/**
* Parses a bracket group of elements delimited by '[' and ']'.
*
* Bracket groups are used to represent response codes.
*/
protected function parseBracketGroup(): ResponseCodeData
{
// Consume the opening '[' token.
$this->advance();
$elements = [];
while (
$this->currentToken
&& ! $this->currentToken instanceof ResponseCodeClose
) {
// Skip CRLF tokens that may appear inside bracket groups.
if ($this->currentToken instanceof Crlf) {
$this->advance();
continue;
}
$elements[] = $this->parseElement();
}
if ($this->currentToken === null) {
throw new ImapParserException('Unterminated bracket group in response');
}
// Consume the closing ']' token.
$this->advance();
return new ResponseCodeData($elements);
}
/**
* Parses a list of elements delimited by '(' and ')'.
*
* Lists are handled recursively, as a list may contain nested lists.
*/
protected function parseList(): ListData
{
// Consume the opening '(' token.
$this->advance();
$elements = [];
// Continue to parse elements until we find the corresponding ')'.
while (
$this->currentToken
&& ! $this->currentToken instanceof ListClose
) {
// Skip CRLF tokens that appear inside lists (after literals).
if ($this->currentToken instanceof Crlf) {
$this->advance();
continue;
}
$elements[] = $this->parseElement();
}
// If we reached the end without finding a closing ')', throw an exception.
if ($this->currentToken === null) {
throw new ImapParserException('Unterminated list in response');
}
// Consume the closing ')' token.
$this->advance();
return new ListData($elements);
}
/**
* Parses a single element, which might be a list or a simple token.
*/
protected function parseElement(): Data|Token|null
{
// If there is no current token, return null.
if ($this->currentToken === null) {
return null;
}
// If the token indicates the start of a list, parse it as a list.
if ($this->currentToken instanceof ListOpen) {
return $this->parseList();
}
// If the token indicates the start of a group, parse it as a group.
if ($this->currentToken instanceof ResponseCodeOpen) {
return $this->parseBracketGroup();
}
// Otherwise, capture the current token.
$token = clone $this->currentToken;
$this->advance();
return $token;
}
/**
* Advance to the next token from the tokenizer.
*/
protected function advance(): void
{
$this->currentToken = $this->tokenizer->nextToken();
}
}

View File

@@ -0,0 +1,527 @@
<?php
namespace DirectoryTree\ImapEngine\Connection;
use BackedEnum;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use DateTimeInterface;
use DirectoryTree\ImapEngine\Enums\ImapSearchKey;
use DirectoryTree\ImapEngine\Support\Str;
class ImapQueryBuilder
{
/**
* The largest UID value allowed by IMAP.
*/
protected const MAX_UID = '4294967295';
/**
* The where conditions for the query.
*/
protected array $wheres = [];
/**
* The date format to use for date based queries.
*/
protected string $dateFormat = 'd-M-Y';
/**
* Add a where "ALL" clause to the query.
*/
public function all(): static
{
return $this->where(ImapSearchKey::All);
}
/**
* Add a where "NEW" clause to the query.
*/
public function new(): static
{
return $this->where(ImapSearchKey::New);
}
/**
* Add a where "OLD" clause to the query.
*/
public function old(): static
{
return $this->where(ImapSearchKey::Old);
}
/**
* Add a where "SEEN" clause to the query.
*/
public function seen(): static
{
return $this->where(ImapSearchKey::Seen);
}
/**
* Add a where "DRAFT" clause to the query.
*/
public function draft(): static
{
return $this->where(ImapSearchKey::Draft);
}
/**
* Add a where "RECENT" clause to the query.
*/
public function recent(): static
{
return $this->where(ImapSearchKey::Recent);
}
/**
* Add a where "UNSEEN" clause to the query.
*/
public function unseen(): static
{
return $this->where(ImapSearchKey::Unseen);
}
/**
* Add a where "FLAGGED" clause to the query.
*/
public function flagged(): static
{
return $this->where(ImapSearchKey::Flagged);
}
/**
* Add a where "DELETED" clause to the query.
*/
public function deleted(): static
{
return $this->where(ImapSearchKey::Deleted);
}
/**
* Add a where "ANSWERED" clause to the query.
*/
public function answered(): static
{
return $this->where(ImapSearchKey::Answered);
}
/**
* Add a where "UNDELETED" clause to the query.
*/
public function undeleted(): static
{
return $this->where(ImapSearchKey::Undeleted);
}
/**
* Add a where "UNFLAGGED" clause to the query.
*/
public function unflagged(): static
{
return $this->where(ImapSearchKey::Unflagged);
}
/**
* Add a where "UNANSWERED" clause to the query.
*/
public function unanswered(): static
{
return $this->where(ImapSearchKey::Unanswered);
}
/**
* Add a where "FROM" clause to the query.
*/
public function from(string $email): static
{
return $this->where(ImapSearchKey::From, $email);
}
/**
* Add a where "TO" clause to the query.
*/
public function to(string $value): static
{
return $this->where(ImapSearchKey::To, $value);
}
/**
* Add a where "CC" clause to the query.
*/
public function cc(string $value): static
{
return $this->where(ImapSearchKey::Cc, $value);
}
/**
* Add a where "BCC" clause to the query.
*/
public function bcc(string $value): static
{
return $this->where(ImapSearchKey::Bcc, $value);
}
/**
* Add a where "BODY" clause to the query.
*/
public function body(string $value): static
{
return $this->where(ImapSearchKey::Body, $value);
}
/**
* Add a where "KEYWORD" clause to the query.
*/
public function keyword(string $value): static
{
return $this->where(ImapSearchKey::Keyword, $value);
}
/**
* Add a where "UNKEYWORD" clause to the query.
*/
public function unkeyword(string $value): static
{
return $this->where(ImapSearchKey::Unkeyword, $value);
}
/**
* Add a where "ON" clause to the query.
*/
public function on(mixed $date): static
{
return $this->where(ImapSearchKey::On, new RawQueryValue(
$this->parseDate($date)->format($this->dateFormat)
));
}
/**
* Add a where "SINCE" clause to the query.
*/
public function since(mixed $date): static
{
return $this->where(ImapSearchKey::Since, new RawQueryValue(
$this->parseDate($date)->format($this->dateFormat)
));
}
/**
* Add a where "BEFORE" clause to the query.
*/
public function before(mixed $value): static
{
return $this->where(ImapSearchKey::Before, new RawQueryValue(
$this->parseDate($value)->format($this->dateFormat)
));
}
/**
* Add a where "SENTON" clause to the query.
*/
public function sentOn(mixed $date): static
{
return $this->where(ImapSearchKey::SentOn, new RawQueryValue(
$this->parseDate($date)->format($this->dateFormat)
));
}
/**
* Add a where "SENTSINCE" clause to the query.
*/
public function sentSince(mixed $date): static
{
return $this->where(ImapSearchKey::SentSince, new RawQueryValue(
$this->parseDate($date)->format($this->dateFormat)
));
}
/**
* Add a where "SENTBEFORE" clause to the query.
*/
public function sentBefore(mixed $date): static
{
return $this->where(ImapSearchKey::SentBefore, new RawQueryValue(
$this->parseDate($date)->format($this->dateFormat)
));
}
/**
* Add a where "SUBJECT" clause to the query.
*/
public function subject(string $value): static
{
return $this->where(ImapSearchKey::Subject, $value);
}
/**
* Add a where "TEXT" clause to the query.
*/
public function text(string $value): static
{
return $this->where(ImapSearchKey::Text, $value);
}
/**
* Add a where "HEADER" clause to the query.
*/
public function header(string $header, string $value): static
{
return $this->where(ImapSearchKey::Header->value." $header", $value);
}
/**
* Add a where "HEADER Message-ID" clause to the query.
*/
public function messageId(string $messageId): static
{
return $this->header('Message-ID', trim(trim($messageId), '<>'));
}
/**
* Add a where "UID" clause to the query.
*/
public function uid(int|string|array $from, int|float|null $to = null): static
{
if ($to === INF) {
$to = self::MAX_UID;
}
return $this->where(ImapSearchKey::Uid, new RawQueryValue(Str::set($from, $to)));
}
/**
* Add a where "LARGER" clause to the query.
*/
public function larger(int $bytes): static
{
return $this->where(ImapSearchKey::Larger, new RawQueryValue($bytes));
}
/**
* Add a where "SMALLER" clause to the query.
*/
public function smaller(int $bytes): static
{
return $this->where(ImapSearchKey::Smaller, new RawQueryValue($bytes));
}
/**
* Add a "where" condition.
*/
public function where(mixed $column, mixed $value = null): static
{
if (is_callable($column)) {
$this->addNestedCondition('AND', $column);
} else {
$this->addBasicCondition('AND', $column, $value);
}
return $this;
}
/**
* Add an "or where" condition.
*/
public function orWhere(mixed $column, mixed $value = null): static
{
if (is_callable($column)) {
$this->addNestedCondition('OR', $column);
} else {
$this->addBasicCondition('OR', $column, $value);
}
return $this;
}
/**
* Add a "where not" condition.
*/
public function whereNot(mixed $column, mixed $value = null): static
{
$this->addBasicCondition('AND', $column, $value, true);
return $this;
}
/**
* Determine if the query has any where conditions.
*/
public function isEmpty(): bool
{
return empty($this->wheres);
}
/**
* Transform the instance into an IMAP-compatible query string.
*/
public function toImap(): string
{
return $this->compileWheres($this->wheres);
}
/**
* Create a new query instance (like Eloquent's newQuery).
*/
protected function newQuery(): static
{
return new static;
}
/**
* Add a basic condition to the query.
*/
protected function addBasicCondition(string $boolean, mixed $column, mixed $value, bool $not = false): void
{
$value = $this->prepareWhereValue($value);
$column = Str::enum($column);
$this->wheres[] = [
'type' => 'basic',
'not' => $not,
'key' => $column,
'value' => $value,
'boolean' => $boolean,
];
}
/**
* Prepare the where value, escaping it as needed.
*/
protected function prepareWhereValue(mixed $value): RawQueryValue|string|null
{
if (is_null($value)) {
return null;
}
if ($value instanceof RawQueryValue) {
return $value;
}
if ($value instanceof BackedEnum) {
$value = $value->value;
}
if ($value instanceof DateTimeInterface) {
$value = Carbon::instance($value);
}
if ($value instanceof CarbonInterface) {
$value = $value->format($this->dateFormat);
}
return Str::escape($value);
}
/**
* Add a nested condition group to the query.
*/
protected function addNestedCondition(string $boolean, callable $callback): void
{
$nested = $this->newQuery();
$callback($nested);
$this->wheres[] = [
'type' => 'nested',
'query' => $nested,
'boolean' => $boolean,
];
}
/**
* Attempt to parse a date string into a Carbon instance.
*/
protected function parseDate(mixed $date): CarbonInterface
{
if ($date instanceof CarbonInterface) {
return $date;
}
return Carbon::parse($date);
}
/**
* Build a single expression node from a basic or nested where.
*
* @param array{type: 'basic'|'nested', boolean: 'AND'|'OR', query: ImapQueryBuilder} $where
*/
protected function makeExpressionNode(array $where): array
{
return match ($where['type']) {
'basic' => [
'expr' => $this->compileBasic($where),
'boolean' => $where['boolean'],
],
'nested' => [
'expr' => $where['query']->toImap(),
'boolean' => $where['boolean'],
]
};
}
/**
* Merge the existing expression with the next expression, respecting the boolean operator.
*
* @param 'AND'|'OR' $boolean
*/
protected function mergeExpressions(string $existing, string $next, string $boolean): string
{
return match ($boolean) {
// AND is implicit just append.
'AND' => $existing.' '.$next,
// IMAP's OR is binary; nest accordingly.
'OR' => 'OR ('.$existing.') ('.$next.')',
};
}
/**
* Recursively compile the wheres array into an IMAP-compatible string.
*/
protected function compileWheres(array $wheres): string
{
if (empty($wheres)) {
return '';
}
// Convert each "where" into a node for later merging.
$exprNodes = array_map(fn (array $where) => (
$this->makeExpressionNode($where)
), $wheres);
// Start with the first expression.
$combined = array_shift($exprNodes)['expr'];
// Merge the rest of the expressions.
foreach ($exprNodes as $node) {
$combined = $this->mergeExpressions(
$combined, $node['expr'], $node['boolean']
);
}
return trim($combined);
}
/**
* Compile a basic where condition into an IMAP-compatible string.
*/
protected function compileBasic(array $where): string
{
$part = strtoupper($where['key']);
if ($where['value'] instanceof RawQueryValue) {
$part .= ' '.$where['value']->value;
} elseif ($where['value']) {
$part .= ' "'.Str::toImapUtf7($where['value']).'"';
}
if ($where['not']) {
$part = 'NOT '.$part;
}
return $part;
}
}

View File

@@ -0,0 +1,511 @@
<?php
namespace DirectoryTree\ImapEngine\Connection;
use DirectoryTree\ImapEngine\Connection\Streams\StreamInterface;
use DirectoryTree\ImapEngine\Connection\Tokens\Atom;
use DirectoryTree\ImapEngine\Connection\Tokens\Crlf;
use DirectoryTree\ImapEngine\Connection\Tokens\EmailAddress;
use DirectoryTree\ImapEngine\Connection\Tokens\ListClose;
use DirectoryTree\ImapEngine\Connection\Tokens\ListOpen;
use DirectoryTree\ImapEngine\Connection\Tokens\Literal;
use DirectoryTree\ImapEngine\Connection\Tokens\Nil;
use DirectoryTree\ImapEngine\Connection\Tokens\Number;
use DirectoryTree\ImapEngine\Connection\Tokens\QuotedString;
use DirectoryTree\ImapEngine\Connection\Tokens\ResponseCodeClose;
use DirectoryTree\ImapEngine\Connection\Tokens\ResponseCodeOpen;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
use DirectoryTree\ImapEngine\Exceptions\ImapParserException;
use DirectoryTree\ImapEngine\Exceptions\ImapStreamException;
class ImapTokenizer
{
/**
* The current position in the buffer.
*/
protected int $position = 0;
/**
* The buffer of characters read from the stream.
*/
protected string $buffer = '';
/**
* Constructor.
*/
public function __construct(
protected StreamInterface $stream
) {}
/**
* Returns the next token from the stream.
*/
public function nextToken(): ?Token
{
$this->skipWhitespace();
$this->ensureBuffer(1);
$char = $this->currentChar();
if ($char === null || $char === '') {
return null;
}
// Check for line feed.
if ($char === "\n") {
// With a valid IMAP response, we should never reach this point,
// but in case we receive a malformed response, we will flush
// the buffer and return null to prevent an infinite loop.
$this->flushBuffer();
return null;
}
// Check for carriage return. (\r\n)
if ($char === "\r") {
$this->advance(); // Consume CR
$this->ensureBuffer(1);
if ($this->currentChar() !== "\n") {
throw new ImapParserException('Expected LF after CR');
}
$this->advance(); // Consume LF (\n)
return new Crlf("\r\n");
}
// Check for parameter list opening.
if ($char === '(') {
$this->advance();
return new ListOpen('(');
}
// Check for a parameter list closing.
if ($char === ')') {
$this->advance();
return new ListClose(')');
}
// Check for a response group open.
if ($char === '[') {
$this->advance();
return new ResponseCodeOpen('[');
}
// Check for response group close.
if ($char === ']') {
$this->advance();
return new ResponseCodeClose(']');
}
// Check for angle bracket open (email addresses).
if ($char === '<') {
$this->advance();
return $this->readEmailAddress();
}
// Check for quoted string.
if ($char === '"') {
return $this->readQuotedString();
}
// Check for literal block open.
if ($char === '{') {
return $this->readLiteral();
}
// Otherwise, parse a number or atom.
return $this->readNumberOrAtom();
}
/**
* Skips whitespace characters (spaces and tabs only, preserving CRLF).
*/
protected function skipWhitespace(): void
{
while (true) {
$this->ensureBuffer(1);
$char = $this->currentChar();
// Break on EOF.
if ($char === null || $char === '') {
break;
}
// Break on CRLF.
if ($char === "\r" || $char === "\n") {
break;
}
// Break on non-whitespace.
if ($char !== ' ' && $char !== "\t") {
break;
}
$this->advance();
}
}
/**
* Reads a quoted string token.
*
* Quoted strings are enclosed in double quotes and may contain escaped characters.
*/
protected function readQuotedString(): QuotedString
{
// Skip the opening quote.
$this->advance();
$value = '';
while (true) {
$this->ensureBuffer(1);
$char = $this->currentChar();
if ($char === null) {
throw new ImapParserException(sprintf(
'Unterminated quoted string at buffer offset %d. Buffer: "%s"',
$this->position,
substr($this->buffer, max(0, $this->position - 10), 20)
));
}
if ($char === '\\') {
$this->advance(); // Skip the backslash.
$this->ensureBuffer(1);
$escapedChar = $this->currentChar();
if ($escapedChar === null) {
throw new ImapParserException('Unterminated escape sequence in quoted string');
}
$value .= $escapedChar;
$this->advance();
continue;
}
if ($char === '"') {
$this->advance(); // Skip the closing quote.
break;
}
$value .= $char;
$this->advance();
}
return new QuotedString($value);
}
/**
* Reads a literal token.
*
* Literal blocks in IMAP have the form {<length>}\r\n<data>.
*/
protected function readLiteral(): Literal
{
// Skip the opening '{'.
$this->advance();
// This will contain the size of the literal block in a sequence of digits.
// {<size>}\r\n<data>
$numStr = '';
while (true) {
$this->ensureBuffer(1);
$char = $this->currentChar();
if ($char === null) {
throw new ImapParserException('Unterminated literal specifier');
}
if ($char === '}') {
$this->advance(); // Skip the '}'.
break;
}
$numStr .= $char;
$this->advance();
}
// Expect carriage return after the literal specifier.
$this->ensureBuffer(2);
// Get the carriage return.
$crlf = substr($this->buffer, $this->position, 2);
if ($crlf !== "\r\n") {
throw new ImapParserException('Expected CRLF after literal specifier');
}
// Skip the CRLF.
$this->advance(2);
$length = (int) $numStr;
// Use any data that is already in our buffer.
$available = strlen($this->buffer) - $this->position;
if ($available >= $length) {
$literal = substr($this->buffer, $this->position, $length);
$this->advance($length);
} else {
// Consume whatever is available without flushing the whole buffer.
$literal = substr($this->buffer, $this->position);
$consumed = strlen($literal);
// Advance the pointer by the number of bytes we took.
$this->advance($consumed);
// Calculate how many bytes are still needed.
$remaining = $length - $consumed;
// Read the missing bytes from the stream.
$data = $this->stream->read($remaining);
if ($data === false || strlen($data) !== $remaining) {
throw new ImapStreamException('Unexpected end of stream while trying to fill the buffer');
}
$literal .= $data;
}
// Verify that the literal length matches the expected length.
if (strlen($literal) !== $length) {
throw new ImapParserException(sprintf(
'Literal length mismatch: expected %d, got %d',
$length,
strlen($literal)
));
}
return new Literal($literal);
}
/**
* Reads a number or atom token.
*/
protected function readNumberOrAtom(): Token
{
$position = $this->position;
// First char must be a digit to even consider a number.
if (! ctype_digit($this->buffer[$position] ?? '')) {
return $this->readAtom();
}
// Walk forward to find the end of the digit run.
while (ctype_digit($this->buffer[$position] ?? '')) {
$position++;
$this->ensureBuffer($position - $this->position + 1);
}
$next = $this->buffer[$position] ?? null;
// If next is EOF or a delimiter, it's a Number.
if ($next === null || $this->isDelimiter($next)) {
return $this->readNumber();
}
// Otherwise it's an Atom.
return $this->readAtom();
}
/**
* Reads a number token.
*
* A number consists of one or more digit characters and represents a numeric value.
*/
protected function readNumber(): Number
{
$start = $this->position;
while (true) {
$this->ensureBuffer(1);
$char = $this->currentChar();
if ($char === null) {
break;
}
if (! ctype_digit($char)) {
break;
}
$this->advance();
}
return new Number(substr($this->buffer, $start, $this->position - $start));
}
/**
* Reads an atom token.
*
* ATOMs are sequences of printable ASCII characters that do not contain delimiters.
*/
protected function readAtom(): Atom
{
$value = '';
while (true) {
$this->ensureBuffer(1);
$char = $this->currentChar();
if ($char === null) {
break;
}
if (! $this->isValidAtomCharacter($char)) {
break;
}
$value .= $char;
$this->advance();
}
if (strcasecmp($value, 'NIL') === 0) {
return new Nil($value);
}
return new Atom($value);
}
/**
* Reads an email address token enclosed in angle brackets.
*
* Email addresses are enclosed in angle brackets ("<" and ">").
*
* For example "<johndoe@email.com>"
*/
protected function readEmailAddress(): ?EmailAddress
{
$value = '';
while (true) {
$this->ensureBuffer(1);
$char = $this->currentChar();
if ($char === null) {
throw new ImapParserException('Unterminated email address, expected ">"');
}
if ($char === '>') {
$this->advance(); // Skip the closing '>'.
break;
}
$value .= $char;
$this->advance();
}
return new EmailAddress($value);
}
/**
* Ensures that at least the given length in characters are available in the buffer.
*/
protected function ensureBuffer(int $length): void
{
// If we have enough data in the buffer, return early.
while ((strlen($this->buffer) - $this->position) < $length) {
$data = $this->stream->fgets();
if ($data === false) {
return;
}
$this->buffer .= $data;
}
}
/**
* Returns the current character in the buffer.
*/
protected function currentChar(): ?string
{
return $this->buffer[$this->position] ?? null;
}
/**
* Advances the internal pointer by $n characters.
*/
protected function advance(int $n = 1): void
{
$this->position += $n;
// If we have consumed the entire buffer, reset it.
if ($this->position >= strlen($this->buffer)) {
$this->flushBuffer();
}
}
/**
* Flush the buffer and reset the position.
*/
protected function flushBuffer(): void
{
$this->buffer = '';
$this->position = 0;
}
/**
* Determine if the given character is a valid atom character.
*/
protected function isValidAtomCharacter(string $char): bool
{
// Get the ASCII code.
$code = ord($char);
// Allow only printable ASCII (32-126).
if ($code < 32 || $code > 126) {
return false;
}
// Delimiters are not allowed inside ATOMs.
if ($this->isDelimiter($char)) {
return false;
}
return true;
}
/**
* Determine if the given character is a delimiter for tokenizing responses.
*/
protected function isDelimiter(string $char): bool
{
// This delimiter list includes additional characters (such as square
// brackets, curly braces, and angle brackets) to ensure that tokens
// like the response code group brackets are split out. This is fine
// for tokenizing responses, even though its more restrictive
// than the IMAP atom definition in RFC 3501 (section 9).
return in_array($char, [' ', '(', ')', '[', ']', '{', '}', '<', '>'], true);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Loggers;
class EchoLogger extends Logger
{
/**
* {@inheritDoc}
*/
public function write(string $message): void
{
echo $message;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Loggers;
class FileLogger extends Logger
{
/**
* Constructor.
*/
public function __construct(
protected string $path
) {}
/**
* {@inheritDoc}
*/
public function write(string $message): void
{
file_put_contents($this->path, $message, FILE_APPEND);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Loggers;
abstract class Logger implements LoggerInterface
{
/**
* Write a message to the log.
*/
abstract protected function write(string $message): void;
/**
* {@inheritDoc}
*/
public function sent(string $message): void
{
$this->write(sprintf('%s: >> %s', $this->date(), $message).PHP_EOL);
}
/**
* {@inheritDoc}
*/
public function received(string $message): void
{
$this->write(sprintf('%s: << %s', $this->date(), $message).PHP_EOL);
}
/**
* Get the current date and time.
*/
protected function date(): string
{
return date('Y-m-d H:i:s');
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Loggers;
interface LoggerInterface
{
/**
* Log when a message is sent.
*/
public function sent(string $message): void;
/**
* Log when a message is received.
*/
public function received(string $message): void;
}

View File

@@ -0,0 +1,14 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Loggers;
class RayLogger extends Logger
{
/**
* {@inheritDoc}
*/
protected function write(string $message): void
{
ray($message);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace DirectoryTree\ImapEngine\Connection;
use Stringable;
class RawQueryValue
{
/**
* Constructor.
*/
public function __construct(
public readonly Stringable|string $value
) {}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Responses;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
class ContinuationResponse extends Response
{
/**
* Get the data tokens.
*
* @return Token[]
*/
public function data(): array
{
return array_slice($this->tokens, 1);
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Responses\Data;
use DirectoryTree\ImapEngine\Connection\Responses\HasTokens;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
use Stringable;
abstract class Data implements Stringable
{
use HasTokens;
/**
* Constructor.
*/
public function __construct(
protected array $tokens
) {}
/**
* Get the tokens.
*
* @return Token[]|Data[]
*/
public function tokens(): array
{
return $this->tokens;
}
/**
* Get the first token.
*/
public function first(): Token|Data|null
{
return $this->tokens[0] ?? null;
}
/**
* Get the last token.
*/
public function last(): Token|Data|null
{
return $this->tokens[count($this->tokens) - 1] ?? null;
}
/**
* Determine if the data contains a specific value.
*/
public function contains(array|string $needles): bool
{
$haystack = $this->values();
foreach ((array) $needles as $needle) {
if (! in_array($needle, $haystack)) {
return false;
}
}
return true;
}
/**
* Get all the token's values.
*/
public function values(): array
{
return array_map(function (Token|Data $token) {
return $token instanceof Data
? $token->values()
: $token->value;
}, $this->tokens);
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Responses\Data;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
class ListData extends Data
{
/**
* Find the immediate successor token of the given field in the list.
*/
public function lookup(string $field): Data|Token|null
{
foreach ($this->tokens as $index => $token) {
if ((string) $token === $field) {
return $this->tokenAt(++$index);
}
}
return null;
}
/**
* Convert alternating key/value tokens to an associative array.
*/
public function toKeyValuePairs(): array
{
$pairs = [];
for ($i = 0; $i < count($this->tokens) - 1; $i += 2) {
$key = strtolower($this->tokens[$i]->value);
$pairs[$key] = $this->tokens[$i + 1]->value;
}
return $pairs;
}
/**
* Get the list as a string.
*/
public function __toString(): string
{
return sprintf('(%s)', implode(
' ', array_map('strval', $this->tokens)
));
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Responses\Data;
class ResponseCodeData extends Data
{
/**
* Get the group as a string.
*/
public function __toString(): string
{
return sprintf('[%s]', implode(
' ', array_map('strval', $this->tokens)
));
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Responses;
use DirectoryTree\ImapEngine\Connection\Responses\Data\Data;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
trait HasTokens
{
/**
* Get the response tokens.
*
* @return Token[]|Data[]
*/
abstract public function tokens(): array;
/**
* Get the response token at the given index.
*/
public function tokenAt(int $index): Token|Data|null
{
return $this->tokens()[$index] ?? null;
}
/**
* Get the response tokens after the given index.
*/
public function tokensAfter(int $index): array
{
return array_slice($this->tokens(), $index);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Responses;
use DirectoryTree\ImapEngine\Connection\Responses\Data\ResponseCodeData;
class MessageResponseParser
{
/**
* Get the UID from a tagged move or copy response.
*/
public static function getUidFromCopy(TaggedResponse $response): ?int
{
if (! $data = $response->tokenAt(2)) {
return null;
}
if (! $data instanceof ResponseCodeData) {
return null;
}
if (! $value = $data->tokenAt(3)?->value) {
return null;
}
return (int) $value;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Responses;
use DirectoryTree\ImapEngine\Connection\Responses\Data\Data;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
use Illuminate\Contracts\Support\Arrayable;
use Stringable;
class Response implements Arrayable, Stringable
{
use HasTokens;
/**
* Constructor.
*/
public function __construct(
protected array $tokens
) {}
/**
* Get the response tokens.
*
* @return Token[]|Data[]
*/
public function tokens(): array
{
return $this->tokens;
}
/**
* Get the instance as an array.
*/
public function toArray(): array
{
return array_map(function (Token|Data $token) {
return $token instanceof Data
? $token->values()
: $token->value;
}, $this->tokens);
}
/**
* Get a JSON representation of the response tokens.
*/
public function __toString(): string
{
return implode(' ', $this->tokens);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Responses;
use DirectoryTree\ImapEngine\Connection\Tokens\Atom;
use DirectoryTree\ImapEngine\Connection\Tokens\Number;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
class TaggedResponse extends Response
{
/**
* Get the response tag.
*/
public function tag(): Atom|Number
{
return $this->tokens[0];
}
/**
* Get the response status token.
*/
public function status(): Atom
{
return $this->tokens[1];
}
/**
* Get the response data tokens.
*
* @return Token[]
*/
public function data(): array
{
return array_slice($this->tokens, 2);
}
/**
* Determine if the response was successful.
*/
public function successful(): bool
{
return strtoupper($this->status()->value) === 'OK';
}
/**
* Determine if the response failed.
*/
public function failed(): bool
{
return in_array(strtoupper($this->status()->value), ['NO', 'BAD']);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Responses;
use DirectoryTree\ImapEngine\Connection\Tokens\Atom;
use DirectoryTree\ImapEngine\Connection\Tokens\Number;
class UntaggedResponse extends Response
{
/**
* Get the response type token.
*/
public function type(): Atom|Number
{
return $this->tokens[1];
}
/**
* Get the data tokens.
*
* @return Atom[]
*/
public function data(): array
{
return array_slice($this->tokens, 2);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace DirectoryTree\ImapEngine\Connection;
use DirectoryTree\ImapEngine\Collections\ResponseCollection;
use DirectoryTree\ImapEngine\Connection\Responses\Response;
class Result
{
/**
* Constructor.
*/
public function __construct(
protected ImapCommand $command,
protected array $responses = [],
) {}
/**
* Get the executed command.
*/
public function command(): ImapCommand
{
return $this->command;
}
/**
* Add a response to the result.
*/
public function addResponse(Response $response): void
{
$this->responses[] = $response;
}
/**
* Get the recently received responses.
*/
public function responses(): ResponseCollection
{
return new ResponseCollection($this->responses);
}
}

View File

@@ -0,0 +1,232 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Streams;
use PHPUnit\Framework\Assert;
use RuntimeException;
class FakeStream implements StreamInterface
{
/**
* Lines queued for testing; each call to fgets() pops the next line.
*
* @var string[]
*/
protected array $buffer = [];
/**
* Data that has been "written" to this fake stream (for assertion).
*
* @var string[]
*/
protected array $written = [];
/**
* The connection info.
*/
protected ?array $connection = null;
/**
* The mock meta info.
*/
protected array $meta = [
'crypto' => [
'protocol' => '',
'cipher_name' => '',
'cipher_bits' => 0,
'cipher_version' => '',
],
'mode' => 'c',
'eof' => false,
'blocked' => false,
'timed_out' => false,
'seekable' => false,
'unread_bytes' => 0,
'stream_type' => 'tcp_socket/unknown',
];
/**
* Feed a line to the stream buffer with a newline character.
*/
public function feed(array|string $lines): self
{
// We'll ensure that each line ends with a CRLF,
// as this is the expected behavior of every
// reply that comes from an IMAP server.
$lines = array_map(fn (string $line) => (
rtrim($line, "\r\n")."\r\n"
), (array) $lines);
array_push($this->buffer, ...$lines);
return $this;
}
/**
* Feed a raw line to the stream buffer.
*/
public function feedRaw(array|string $lines): self
{
array_push($this->buffer, ...(array) $lines);
return $this;
}
/**
* Set the timed out status.
*/
public function setMeta(string $attribute, mixed $value): self
{
if (! isset($this->meta[$attribute])) {
throw new RuntimeException(
"Unknown metadata attribute: {$attribute}"
);
}
if (gettype($this->meta[$attribute]) !== gettype($value)) {
throw new RuntimeException(
"Metadata attribute {$attribute} must be of type ".gettype($this->meta[$attribute])
);
}
$this->meta[$attribute] = $value;
return $this;
}
/**
* {@inheritDoc}
*/
public function open(?string $transport = null, ?string $host = null, ?int $port = null, ?int $timeout = null, array $options = []): bool
{
$this->connection = compact('transport', 'host', 'port', 'timeout', 'options');
return true;
}
/**
* {@inheritDoc}
*/
public function close(): void
{
$this->buffer = [];
$this->connection = null;
}
/**
* {@inheritDoc}
*/
public function read(int $length): string|false
{
if (! $this->opened()) {
return false;
}
if ($this->meta['eof'] && empty($this->buffer)) {
return false; // EOF and no data left. Indicate end of stream.
}
$data = implode('', $this->buffer);
$availableLength = strlen($data);
if ($availableLength === 0) {
// No data available right now (but not EOF).
// Simulate non-blocking behavior.
return '';
}
$bytesToRead = min($length, $availableLength);
$result = substr($data, 0, $bytesToRead);
$remainingData = substr($data, $bytesToRead);
$this->buffer = $remainingData !== '' ? [$remainingData] : [];
return $result;
}
/**
* {@inheritDoc}
*/
public function fgets(): string|false
{
if (! $this->opened()) {
return false;
}
// Simulate timeout/eof checks.
if ($this->meta['timed_out'] || $this->meta['eof']) {
return false;
}
return array_shift($this->buffer) ?? false;
}
/**
* {@inheritDoc}
*/
public function fwrite(string $data): int|false
{
if (! $this->opened()) {
return false;
}
$this->written[] = $data;
return strlen($data);
}
/**
* {@inheritDoc}
*/
public function meta(): array
{
return $this->meta;
}
/**
* {@inheritDoc}
*/
public function opened(): bool
{
return (bool) $this->connection;
}
/**
* {@inheritDoc}
*/
public function setTimeout(int $seconds): bool
{
return true;
}
/**
* {@inheritDoc}
*/
public function setSocketSetCrypto(bool $enabled, ?int $method): bool|int
{
return true;
}
/**
* Assert that the given data was written to the stream.
*/
public function assertWritten(string $string): void
{
$found = false;
foreach ($this->written as $index => $written) {
if (str_contains($written, $string)) {
unset($this->written[$index]);
$found = true;
break;
}
}
Assert::assertTrue($found, "Failed asserting that the string '{$string}' was written to the stream.");
}
}

View File

@@ -0,0 +1,120 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Streams;
use DirectoryTree\ImapEngine\Exceptions\ImapConnectionFailedException;
class ImapStream implements StreamInterface
{
/**
* The underlying PHP stream resource.
*
* @var resource|null
*/
protected mixed $stream = null;
/**
* {@inheritDoc}
*/
public function open(string $transport, string $host, int $port, int $timeout, array $options = []): bool
{
$this->stream = @stream_socket_client(
$address = "{$transport}://{$host}:{$port}",
$errno,
$errstr,
$timeout,
STREAM_CLIENT_CONNECT,
stream_context_create($options)
);
if (! $this->stream) {
throw new ImapConnectionFailedException("Unable to connect to {$address} ({$errstr})", $errno);
}
return true;
}
/**
* {@inheritDoc}
*/
public function close(): void
{
if ($this->opened()) {
fclose($this->stream);
}
$this->stream = null;
}
/**
* {@inheritDoc}
*/
public function read(int $length): string|false
{
if (! $this->opened()) {
return false;
}
$data = '';
while (strlen($data) < $length && ! feof($this->stream)) {
$chunk = fread($this->stream, $length - strlen($data));
if ($chunk === false) {
return false;
}
$data .= $chunk;
}
return $data;
}
/**
* {@inheritDoc}
*/
public function fgets(): string|false
{
return $this->opened() ? fgets($this->stream) : false;
}
/**
* {@inheritDoc}
*/
public function fwrite(string $data): int|false
{
return $this->opened() ? fwrite($this->stream, $data) : false;
}
/**
* {@inheritDoc}
*/
public function meta(): array
{
return $this->opened() ? stream_get_meta_data($this->stream) : [];
}
/**
* {@inheritDoc}
*/
public function opened(): bool
{
return is_resource($this->stream);
}
/**
* {@inheritDoc}
*/
public function setTimeout(int $seconds): bool
{
return stream_set_timeout($this->stream, $seconds);
}
/**
* {@inheritDoc}
*/
public function setSocketSetCrypto(bool $enabled, ?int $method): bool|int
{
return stream_socket_enable_crypto($this->stream, $enabled, $method);
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Streams;
interface StreamInterface
{
/**
* Open the underlying stream.
*/
public function open(string $transport, string $host, int $port, int $timeout, array $options = []): bool;
/**
* Close the underlying stream.
*/
public function close(): void;
/**
* Read data from the stream.
*/
public function read(int $length): string|false;
/**
* Read a single line from the stream.
*/
public function fgets(): string|false;
/**
* Write data to the stream.
*/
public function fwrite(string $data): int|false;
/**
* Return meta info (like stream_get_meta_data).
*/
public function meta(): array;
/**
* Determine if the stream is open.
*/
public function opened(): bool;
/**
* Set the timeout on the stream.
*/
public function setTimeout(int $seconds): bool;
/**
* Set encryption state on an already connected socked.
*/
public function setSocketSetCrypto(bool $enabled, ?int $method): bool|int;
}

View File

@@ -0,0 +1,8 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
/**
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-atom
*/
class Atom extends Token {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
class Crlf extends Token {}

View File

@@ -0,0 +1,14 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
class EmailAddress extends Token
{
/**
* Get the token's value.
*/
public function __toString(): string
{
return '<'.$this->value.'>';
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
class ListClose extends Token {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
class ListOpen extends Token {}

View File

@@ -0,0 +1,14 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
class Literal extends Token
{
/**
* Get the token's value.
*/
public function __toString(): string
{
return sprintf("{%d}\r\n%s", strlen($this->value), $this->value);
}
}

View File

@@ -0,0 +1,8 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
/**
* @see https://datatracker.ietf.org/doc/html/rfc9051#name-nil
*/
class Nil extends Atom {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
class Number extends Token {}

View File

@@ -0,0 +1,14 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
class QuotedString extends Token
{
/**
* Get the token's value.
*/
public function __toString(): string
{
return '"'.$this->value.'"';
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
class ResponseCodeClose extends Token {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
class ResponseCodeOpen extends Token {}

View File

@@ -0,0 +1,39 @@
<?php
namespace DirectoryTree\ImapEngine\Connection\Tokens;
use Stringable;
abstract class Token implements Stringable
{
/**
* Constructor.
*/
public function __construct(
public string $value,
) {}
/**
* Determine if the token is the given value.
*/
public function is(string $value): bool
{
return $this->value === $value;
}
/**
* Determine if the token is not the given value.
*/
public function isNot(string $value): bool
{
return ! $this->is($value);
}
/**
* Get the token's value.
*/
public function __toString(): string
{
return $this->value;
}
}

View File

@@ -0,0 +1,122 @@
<?php
namespace DirectoryTree\ImapEngine;
use DirectoryTree\ImapEngine\Connection\Responses\Data\ListData;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
use DirectoryTree\ImapEngine\Enums\ContentDispositionType;
use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;
/**
* @see https://datatracker.ietf.org/doc/html/rfc2183
*/
class ContentDisposition implements Arrayable, JsonSerializable
{
/**
* Constructor.
*/
public function __construct(
protected ContentDispositionType $type,
protected array $parameters = [],
) {}
/**
* Parse the disposition from tokens.
*
* @param array<Token|ListData> $tokens
*/
public static function parse(array $tokens): ?static
{
for ($i = 8; $i < count($tokens); $i++) {
if (! $tokens[$i] instanceof ListData) {
continue;
}
$innerTokens = $tokens[$i]->tokens();
if (! isset($innerTokens[0]) || ! $innerTokens[0] instanceof Token) {
continue;
}
if (! $type = ContentDispositionType::tryFrom(strtolower($innerTokens[0]->value))) {
continue;
}
$parameters = isset($innerTokens[1]) && $innerTokens[1] instanceof ListData
? $innerTokens[1]->toKeyValuePairs()
: [];
return new self($type, $parameters);
}
return null;
}
/**
* Get the disposition type.
*/
public function type(): ContentDispositionType
{
return $this->type;
}
/**
* Get the disposition parameters.
*/
public function parameters(): array
{
return $this->parameters;
}
/**
* Get a specific parameter value.
*/
public function parameter(string $name): ?string
{
return $this->parameters[strtolower($name)] ?? null;
}
/**
* Get the filename parameter.
*/
public function filename(): ?string
{
return $this->parameters['filename'] ?? null;
}
/**
* Determine if this is an attachment disposition.
*/
public function isAttachment(): bool
{
return $this->type === ContentDispositionType::Attachment;
}
/**
* Determine if this is an inline disposition.
*/
public function isInline(): bool
{
return $this->type === ContentDispositionType::Inline;
}
/**
* Get the array representation.
*/
public function toArray(): array
{
return [
'type' => $this->type->value,
'parameters' => $this->parameters,
];
}
/**
* Get the JSON representation.
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}

View File

@@ -0,0 +1,99 @@
<?php
namespace DirectoryTree\ImapEngine;
use DateTimeInterface;
use Stringable;
use Symfony\Component\Mime\Email;
class DraftMessage implements Stringable
{
/**
* The underlying Symfony Email instance.
*/
protected Email $message;
/**
* Constructor.
*/
public function __construct(
protected ?string $from = null,
protected array|string $to = [],
protected array|string $cc = [],
protected array|string $bcc = [],
protected ?string $subject = null,
protected ?string $text = null,
protected ?string $html = null,
protected array $headers = [],
protected array $attachments = [],
protected ?DateTimeInterface $date = null,
) {
$this->message = new Email;
if ($this->from) {
$this->message->from($this->from);
}
if ($this->subject) {
$this->message->subject($this->subject);
}
if ($this->text) {
$this->message->text($this->text);
}
if ($this->html) {
$this->message->html($this->html);
}
if ($this->date) {
$this->message->date($this->date);
}
if (! empty($this->to)) {
$this->message->to(...(array) $this->to);
}
if (! empty($this->cc)) {
$this->message->cc(...(array) $this->cc);
}
if (! empty($this->bcc)) {
$this->message->bcc(...(array) $this->bcc);
}
foreach ($this->attachments as $attachment) {
match (true) {
$attachment instanceof Attachment => $this->message->attach(
$attachment->contents(),
$attachment->filename(),
$attachment->contentType()
),
is_resource($attachment) => $this->message->attach($attachment),
default => $this->message->attachFromPath($attachment),
};
}
foreach ($this->headers as $name => $value) {
$this->message->getHeaders()->addTextHeader($name, $value);
}
}
/**
* Get the underlying Symfony Email instance.
*/
public function email(): Email
{
return $this->message;
}
/**
* Get the email as a string.
*/
public function __toString(): string
{
return $this->message->toString();
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace DirectoryTree\ImapEngine\Enums;
/**
* @see https://datatracker.ietf.org/doc/html/rfc2183
*/
enum ContentDispositionType: string
{
case Inline = 'inline';
case Attachment = 'attachment';
}

View File

@@ -0,0 +1,9 @@
<?php
namespace DirectoryTree\ImapEngine\Enums;
enum ImapFetchIdentifier
{
case Uid;
case MessageNumber;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace DirectoryTree\ImapEngine\Enums;
enum ImapFlag: string
{
case Seen = '\Seen';
case Draft = '\Draft';
case Recent = '\Recent';
case Flagged = '\Flagged';
case Deleted = '\Deleted';
case Answered = '\Answered';
}

View File

@@ -0,0 +1,39 @@
<?php
namespace DirectoryTree\ImapEngine\Enums;
enum ImapSearchKey: string
{
case Cc = 'CC';
case On = 'ON';
case To = 'TO';
case All = 'ALL';
case New = 'NEW';
case Old = 'OLD';
case Bcc = 'BCC';
case Uid = 'UID';
case Seen = 'SEEN';
case Body = 'BODY';
case From = 'FROM';
case Text = 'TEXT';
case Draft = 'DRAFT';
case Since = 'SINCE';
case SentOn = 'SENTON';
case SentSince = 'SENTSINCE';
case SentBefore = 'SENTBEFORE';
case Recent = 'RECENT';
case Unseen = 'UNSEEN';
case Before = 'BEFORE';
case Header = 'HEADER';
case Larger = 'LARGER';
case Deleted = 'DELETED';
case Flagged = 'FLAGGED';
case Keyword = 'KEYWORD';
case Unkeyword = 'UNKEYWORD';
case Subject = 'SUBJECT';
case Smaller = 'SMALLER';
case Answered = 'ANSWERED';
case Undeleted = 'UNDELETED';
case Unflagged = 'UNFLAGGED';
case Unanswered = 'UNANSWERED';
}

View File

@@ -0,0 +1,14 @@
<?php
namespace DirectoryTree\ImapEngine\Enums;
enum ImapSortKey: string
{
case Cc = 'CC';
case To = 'TO';
case Date = 'DATE';
case From = 'FROM';
case Size = 'SIZE';
case Arrival = 'ARRIVAL';
case Subject = 'SUBJECT';
}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
class Exception extends \Exception {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
class ImapCapabilityException extends Exception {}

View File

@@ -0,0 +1,48 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
use DirectoryTree\ImapEngine\Connection\ImapCommand;
use DirectoryTree\ImapEngine\Connection\Responses\Response;
class ImapCommandException extends Exception
{
/**
* The IMAP response.
*/
protected Response $response;
/**
* The failed IMAP command.
*/
protected ImapCommand $command;
/**
* Make a new instance from a failed command and response.
*/
public static function make(ImapCommand $command, Response $response): static
{
$exception = new static(sprintf('IMAP command "%s" failed. Response: "%s"', $command, $response));
$exception->command = $command;
$exception->response = $response;
return $exception;
}
/**
* Get the failed IMAP command.
*/
public function command(): ImapCommand
{
return $this->command;
}
/**
* Get the IMAP response.
*/
public function response(): Response
{
return $this->response;
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
class ImapConnectionClosedException extends ImapConnectionException {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
abstract class ImapConnectionException extends Exception {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
class ImapConnectionFailedException extends ImapConnectionException {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
class ImapConnectionTimedOutException extends ImapConnectionException {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
class ImapParserException extends Exception {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
class ImapResponseException extends Exception {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
class ImapStreamException extends Exception {}

View File

@@ -0,0 +1,5 @@
<?php
namespace DirectoryTree\ImapEngine\Exceptions;
class RuntimeException extends \RuntimeException {}

View File

@@ -0,0 +1,99 @@
<?php
namespace DirectoryTree\ImapEngine;
use BackedEnum;
use BadMethodCallException;
class FileMessage implements MessageInterface
{
use HasFlags, HasMessageAccessors, HasParsedMessage;
/**
* Constructor.
*/
public function __construct(
protected string $contents
) {}
/**
* {@inheritDoc}
*/
public function uid(): int
{
throw new BadMethodCallException('FileMessage does not support a UID');
}
/**
* {@inheritDoc}
*/
public function size(): ?int
{
return strlen($this->contents);
}
/**
* {@inheritDoc}
*/
public function flag(BackedEnum|string $flag, string $operation, bool $expunge = false): void
{
throw new BadMethodCallException('FileMessage does not support flagging');
}
/**
* Get the string representation of the message.
*/
public function __toString(): string
{
return $this->contents;
}
/**
* Determine if this message is equal to another.
*/
public function is(MessageInterface $message): bool
{
return $message instanceof self
&& $this->contents === $message->contents;
}
/**
* Get the message flags.
*/
public function flags(): array
{
return [];
}
/**
* {@inheritDoc}
*/
public function bodyStructure(): ?BodyStructureCollection
{
return null;
}
/**
* {@inheritDoc}
*/
public function hasBodyStructure(): bool
{
return false;
}
/**
* {@inheritDoc}
*/
public function bodyPart(string $partNumber, bool $peek = true): ?string
{
throw new BadMethodCallException('FileMessage does not support fetching body parts');
}
/**
* Determine if the message is empty.
*/
public function isEmpty(): bool
{
return empty($this->contents);
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace DirectoryTree\ImapEngine;
use BackedEnum;
interface FlaggableInterface
{
/**
* Mark the message as read. Alias for markSeen.
*/
public function markRead(): void;
/**
* Mark the message as unread. Alias for unmarkSeen.
*/
public function markUnread(): void;
/**
* Mark the message as seen.
*/
public function markSeen(): void;
/**
* Unmark the seen flag.
*/
public function unmarkSeen(): void;
/**
* Mark the message as answered.
*/
public function markAnswered(): void;
/**
* Unmark the answered flag.
*/
public function unmarkAnswered(): void;
/**
* Mark the message as flagged.
*/
public function markFlagged(): void;
/**
* Unmark the flagged flag.
*/
public function unmarkFlagged(): void;
/**
* Mark the message as deleted.
*/
public function markDeleted(bool $expunge = false): void;
/**
* Unmark the deleted flag.
*/
public function unmarkDeleted(): void;
/**
* Mark the message as a draft.
*/
public function markDraft(): void;
/**
* Unmark the draft flag.
*/
public function unmarkDraft(): void;
/**
* Mark the message as recent.
*/
public function markRecent(): void;
/**
* Unmark the recent flag.
*/
public function unmarkRecent(): void;
/**
* Determine if the message is marked as seen.
*/
public function isSeen(): bool;
/**
* Determine if the message is marked as answered.
*/
public function isAnswered(): bool;
/**
* Determine if the message is flagged.
*/
public function isFlagged(): bool;
/**
* Determine if the message is marked as deleted.
*/
public function isDeleted(): bool;
/**
* Determine if the message is marked as a draft.
*/
public function isDraft(): bool;
/**
* Determine if the message is marked as recent.
*/
public function isRecent(): bool;
/**
* Get the message's flags.
*
* @return string[]
*/
public function flags(): array;
/**
* Determine if the message has the given flag.
*/
public function hasFlag(BackedEnum|string $flag): bool;
/**
* Add or remove a flag from the message.
*
* @param '+'|'-' $operation
*/
public function flag(BackedEnum|string $flag, string $operation, bool $expunge = false): void;
}

View File

@@ -0,0 +1,278 @@
<?php
namespace DirectoryTree\ImapEngine;
use Closure;
use DirectoryTree\ImapEngine\Connection\ImapQueryBuilder;
use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse;
use DirectoryTree\ImapEngine\Enums\ImapFetchIdentifier;
use DirectoryTree\ImapEngine\Exceptions\Exception;
use DirectoryTree\ImapEngine\Exceptions\ImapCapabilityException;
use DirectoryTree\ImapEngine\Support\Str;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\ItemNotFoundException;
use JsonSerializable;
class Folder implements Arrayable, FolderInterface, JsonSerializable
{
use ComparesFolders;
/**
* Constructor.
*/
public function __construct(
protected Mailbox $mailbox,
protected string $path,
protected array $flags = [],
protected string $delimiter = '/',
) {}
/**
* Get the folder's mailbox.
*/
public function mailbox(): Mailbox
{
return $this->mailbox;
}
/**
* Get the folder path.
*/
public function path(): string
{
return $this->path;
}
/**
* Get the folder flags.
*
* @return string[]
*/
public function flags(): array
{
return $this->flags;
}
/**
* {@inheritDoc}
*/
public function delimiter(): string
{
return $this->delimiter;
}
/**
* {@inheritDoc}
*/
public function name(): string
{
return Str::fromImapUtf7(
last(explode($this->delimiter, $this->path))
);
}
/**
* {@inheritDoc}
*/
public function is(FolderInterface $folder): bool
{
return $this->isSameFolder($this, $folder);
}
/**
* {@inheritDoc}
*/
public function messages(): MessageQuery
{
// Ensure the folder is selected.
$this->select(true);
return new MessageQuery($this, new ImapQueryBuilder);
}
/**
* {@inheritDoc}
*/
public function idle(callable $callback, ?callable $query = null, callable|int $timeout = 300): void
{
if (! in_array('IDLE', $this->mailbox->capabilities())) {
throw new ImapCapabilityException('Unable to IDLE. IMAP server does not support IDLE capability.');
}
// Normalize timeout into a closure.
if (is_callable($timeout) && ! $timeout instanceof Closure) {
$timeout = $timeout(...);
}
// The message query to use when fetching messages.
$query ??= fn (MessageQuery $query) => $query;
// Fetch the message by message number.
$fetch = fn (int $msgn) => (
$query($this->messages())->findOrFail($msgn, ImapFetchIdentifier::MessageNumber)
);
(new Idle(clone $this->mailbox, $this->path, $timeout))->await(
function (int $msgn) use ($callback, $fetch) {
if (! $this->mailbox->connected()) {
$this->mailbox->connect();
}
try {
$message = $fetch($msgn);
} catch (ItemNotFoundException) {
// The message wasn't found. We will skip
// it and continue awaiting new messages.
return;
} catch (Exception) {
// Something else happened. We will attempt
// reconnecting and re-fetching the message.
$this->mailbox->reconnect();
$message = $fetch($msgn);
}
$callback($message);
}
);
}
/**
* {@inheritDoc}
*/
public function poll(callable $callback, ?callable $query = null, callable|int $frequency = 60): void
{
(new Poll(clone $this->mailbox, $this->path, $frequency))->start(
function (MessageInterface $message) use ($callback) {
if (! $this->mailbox->connected()) {
$this->mailbox->connect();
}
try {
$callback($message);
} catch (Exception) {
// Something unexpected happened. We will attempt
// reconnecting and continue polling for messages.
$this->mailbox->reconnect();
}
},
$query ?? fn (MessageQuery $query) => $query
);
}
/**
* {@inheritDoc}
*/
public function move(string $newPath): void
{
$this->mailbox->connection()->rename($this->path, $newPath);
$this->path = $newPath;
}
/**
* {@inheritDoc}
*/
public function select(bool $force = false): void
{
$this->mailbox->select($this, $force);
}
/**
* {@inheritDoc}
*/
public function quota(): array
{
if (! in_array('QUOTA', $this->mailbox->capabilities())) {
throw new ImapCapabilityException(
'Unable to fetch mailbox quotas. IMAP server does not support QUOTA capability.'
);
}
$responses = $this->mailbox->connection()->quotaRoot($this->path);
$values = [];
foreach ($responses as $response) {
$resource = $response->tokenAt(2);
$tokens = $response->tokenAt(3)->tokens();
for ($i = 0; $i + 2 < count($tokens); $i += 3) {
$values[$resource->value][$tokens[$i]->value] = [
'usage' => (int) $tokens[$i + 1]->value,
'limit' => (int) $tokens[$i + 2]->value,
];
}
}
return $values;
}
/**
* {@inheritDoc}
*/
public function status(): array
{
$response = $this->mailbox->connection()->status($this->path);
$tokens = $response->tokenAt(3)->tokens();
$values = [];
// Tokens are expected to alternate between keys and values.
for ($i = 0; $i < count($tokens); $i += 2) {
$values[$tokens[$i]->value] = $tokens[$i + 1]->value;
}
return $values;
}
/**
* {@inheritDoc}
*/
public function examine(): array
{
return $this->mailbox->connection()->examine($this->path)->map(
fn (UntaggedResponse $response) => $response->toArray()
)->all();
}
/**
* {@inheritDoc}
*/
public function expunge(): array
{
return $this->mailbox->connection()->expunge()->map(
fn (UntaggedResponse $response) => $response->tokenAt(1)->value
)->all();
}
/**
* {@inheritDoc}
*/
public function delete(): void
{
$this->mailbox->connection()->delete($this->path);
}
/**
* Get the array representation of the folder.
*/
public function toArray(): array
{
return [
'path' => $this->path,
'flags' => $this->flags,
'delimiter' => $this->delimiter,
];
}
/**
* Get the JSON representation of the folder.
*/
public function jsonSerialize(): array
{
return $this->toArray();
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace DirectoryTree\ImapEngine;
interface FolderInterface
{
/**
* Get the folder's mailbox.
*/
public function mailbox(): MailboxInterface;
/**
* Get the folder path.
*/
public function path(): string;
/**
* Get the folder flags.
*
* @return string[]
*/
public function flags(): array;
/**
* Get the folder delimiter.
*/
public function delimiter(): string;
/**
* Get the folder name.
*/
public function name(): string;
/**
* Determine if the current folder is the same as the given.
*/
public function is(FolderInterface $folder): bool;
/**
* Begin querying for messages.
*/
public function messages(): MessageQueryInterface;
/**
* Begin idling on the current folder for the given timeout in seconds.
*/
public function idle(callable $callback, ?callable $query = null, callable|int $timeout = 300): void;
/**
* Begin polling for new messages at the given frequency in seconds.
*/
public function poll(callable $callback, ?callable $query = null, callable|int $frequency = 60): void;
/**
* Move or rename the current folder.
*/
public function move(string $newPath): void;
/**
* Select the current folder.
*/
public function select(bool $force = false): void;
/**
* Get the folder's quotas.
*/
public function quota(): array;
/**
* Get the folder's status.
*/
public function status(): array;
/**
* Examine the current folder and get detailed status information.
*/
public function examine(): array;
/**
* Expunge the mailbox and return the expunged message sequence numbers.
*/
public function expunge(): array;
/**
* Delete the current folder.
*/
public function delete(): void;
}

View File

@@ -0,0 +1,68 @@
<?php
namespace DirectoryTree\ImapEngine;
use DirectoryTree\ImapEngine\Collections\FolderCollection;
use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse;
use DirectoryTree\ImapEngine\Support\Str;
class FolderRepository implements FolderRepositoryInterface
{
/**
* Constructor.
*/
public function __construct(
protected Mailbox $mailbox
) {}
/**
* {@inheritDoc}
*/
public function find(string $path): ?FolderInterface
{
return $this->get($path)->first();
}
/**
* {@inheritDoc}
*/
public function findOrFail(string $path): FolderInterface
{
return $this->get($path)->firstOrFail();
}
/**
* {@inheritDoc}
*/
public function create(string $path): FolderInterface
{
$this->mailbox->connection()->create(
Str::toImapUtf7($path)
);
return $this->find($path);
}
/**
* {@inheritDoc}
*/
public function firstOrCreate(string $path): FolderInterface
{
return $this->find($path) ?? $this->create($path);
}
/**
* {@inheritDoc}
*/
public function get(?string $match = '*', ?string $reference = ''): FolderCollection
{
return $this->mailbox->connection()->list($reference, Str::toImapUtf7($match))->map(
fn (UntaggedResponse $response) => new Folder(
mailbox: $this->mailbox,
path: $response->tokenAt(4)->value,
flags: $response->tokenAt(2)->values(),
delimiter: $response->tokenAt(3)->value,
)
)->pipeInto(FolderCollection::class);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace DirectoryTree\ImapEngine;
use DirectoryTree\ImapEngine\Collections\FolderCollection;
interface FolderRepositoryInterface
{
/**
* Find a folder.
*/
public function find(string $path): ?FolderInterface;
/**
* Find a folder or throw an exception.
*/
public function findOrFail(string $path): FolderInterface;
/**
* Create a new folder.
*/
public function create(string $path): FolderInterface;
/**
* Find or create a folder.
*/
public function firstOrCreate(string $path): FolderInterface;
/**
* Get the mailboxes folders.
*/
public function get(?string $match = '*', ?string $reference = ''): FolderCollection;
}

View File

@@ -0,0 +1,188 @@
<?php
namespace DirectoryTree\ImapEngine;
use BackedEnum;
use DirectoryTree\ImapEngine\Enums\ImapFlag;
use DirectoryTree\ImapEngine\Support\Str;
trait HasFlags
{
/**
* {@inheritDoc}
*/
public function markRead(): void
{
$this->markSeen();
}
/**
* {@inheritDoc}
*/
public function markUnread(): void
{
$this->unmarkSeen();
}
/**
* {@inheritDoc}
*/
public function markSeen(): void
{
$this->flag(ImapFlag::Seen, '+');
}
/**
* {@inheritDoc}
*/
public function unmarkSeen(): void
{
$this->flag(ImapFlag::Seen, '-');
}
/**
* {@inheritDoc}
*/
public function markAnswered(): void
{
$this->flag(ImapFlag::Answered, '+');
}
/**
* {@inheritDoc}
*/
public function unmarkAnswered(): void
{
$this->flag(ImapFlag::Answered, '-');
}
/**
* {@inheritDoc}
*/
public function markFlagged(): void
{
$this->flag(ImapFlag::Flagged, '+');
}
/**
* {@inheritDoc}
*/
public function unmarkFlagged(): void
{
$this->flag(ImapFlag::Flagged, '-');
}
/**
* {@inheritDoc}
*/
public function markDeleted(bool $expunge = false): void
{
$this->flag(ImapFlag::Deleted, '+', $expunge);
}
/**
* {@inheritDoc}
*/
public function unmarkDeleted(): void
{
$this->flag(ImapFlag::Deleted, '-');
}
/**
* {@inheritDoc}
*/
public function markDraft(): void
{
$this->flag(ImapFlag::Draft, '+');
}
/**
* {@inheritDoc}
*/
public function unmarkDraft(): void
{
$this->flag(ImapFlag::Draft, '-');
}
/**
* {@inheritDoc}
*/
public function markRecent(): void
{
$this->flag(ImapFlag::Recent, '+');
}
/**
* {@inheritDoc}
*/
public function unmarkRecent(): void
{
$this->flag(ImapFlag::Recent, '-');
}
/**
* {@inheritDoc}
*/
public function isSeen(): bool
{
return $this->hasFlag(ImapFlag::Seen);
}
/**
* {@inheritDoc}
*/
public function isAnswered(): bool
{
return $this->hasFlag(ImapFlag::Answered);
}
/**
* {@inheritDoc}
*/
public function isFlagged(): bool
{
return $this->hasFlag(ImapFlag::Flagged);
}
/**
* {@inheritDoc}
*/
public function isDeleted(): bool
{
return $this->hasFlag(ImapFlag::Deleted);
}
/**
* {@inheritDoc}
*/
public function isDraft(): bool
{
return $this->hasFlag(ImapFlag::Draft);
}
/**
* {@inheritDoc}
*/
public function isRecent(): bool
{
return $this->hasFlag(ImapFlag::Recent);
}
/**
* {@inheritDoc}
*/
public function hasFlag(BackedEnum|string $flag): bool
{
return in_array(Str::enum($flag), $this->flags());
}
/**
* {@inheritDoc}
*/
abstract public function flags(): array;
/**
* {@inheritDoc}
*/
abstract public function flag(BackedEnum|string $flag, string $operation, bool $expunge = false): void;
}

View File

@@ -0,0 +1,177 @@
<?php
namespace DirectoryTree\ImapEngine;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use ZBateson\MailMimeParser\Header\DateHeader;
use ZBateson\MailMimeParser\Header\HeaderConsts;
use ZBateson\MailMimeParser\Header\IHeader;
use ZBateson\MailMimeParser\Header\IHeaderPart;
use ZBateson\MailMimeParser\IMessage;
trait HasMessageAccessors
{
/**
* Parse the message into a MailMimeMessage instance.
*/
abstract public function parse(): IMessage;
/**
* Get addresses from the given header.
*
* @return Address[]
*/
abstract public function addresses(string $header): array;
/**
* Get a header from the message.
*/
abstract public function header(string $name, int $offset = 0): ?IHeader;
/**
* Get the message date and time.
*/
public function date(): ?CarbonInterface
{
if (! $header = $this->header(HeaderConsts::DATE)) {
return null;
}
if (! $header instanceof DateHeader) {
return null;
}
if (! $date = $header->getDateTime()) {
return null;
}
return Carbon::instance($date);
}
/**
* Get the message's message-id.
*/
public function messageId(): ?string
{
return $this->header(HeaderConsts::MESSAGE_ID)?->getValue();
}
/**
* Get the message's subject.
*/
public function subject(): ?string
{
return $this->header(HeaderConsts::SUBJECT)?->getValue();
}
/**
* Get the FROM address.
*/
public function from(): ?Address
{
return head($this->addresses(HeaderConsts::FROM)) ?: null;
}
/**
* Get the SENDER address.
*/
public function sender(): ?Address
{
return head($this->addresses(HeaderConsts::SENDER)) ?: null;
}
/**
* Get the REPLY-TO address.
*/
public function replyTo(): ?Address
{
return head($this->addresses(HeaderConsts::REPLY_TO)) ?: null;
}
/**
* Get the IN-REPLY-TO message identifier(s).
*
* @return string[]
*/
public function inReplyTo(): array
{
$parts = $this->header(HeaderConsts::IN_REPLY_TO)?->getParts() ?? [];
$values = array_map(fn (IHeaderPart $part) => $part->getValue(), $parts);
return array_values(array_filter($values));
}
/**
* Get the TO addresses.
*
* @return Address[]
*/
public function to(): array
{
return $this->addresses(HeaderConsts::TO);
}
/**
* Get the CC addresses.
*
* @return Address[]
*/
public function cc(): array
{
return $this->addresses(HeaderConsts::CC);
}
/**
* Get the BCC addresses.
*
* @return Address[]
*/
public function bcc(): array
{
return $this->addresses(HeaderConsts::BCC);
}
/**
* Get the message's HTML content.
*/
public function html(): ?string
{
return $this->parse()->getHtmlContent();
}
/**
* Get the message's text content.
*/
public function text(): ?string
{
return $this->parse()->getTextContent();
}
/**
* Get the message's attachments.
*
* @return Attachment[]
*/
public function attachments(): array
{
return Attachment::parsed($this);
}
/**
* Determine if the message has attachments.
*/
public function hasAttachments(): bool
{
return $this->attachmentCount() > 0;
}
/**
* Get the count of attachments.
*/
public function attachmentCount(): int
{
return $this->parse()->getAttachmentCount();
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace DirectoryTree\ImapEngine;
use DirectoryTree\ImapEngine\Exceptions\RuntimeException;
use ZBateson\MailMimeParser\Header\IHeader;
use ZBateson\MailMimeParser\Header\IHeaderPart;
use ZBateson\MailMimeParser\Header\Part\AddressPart;
use ZBateson\MailMimeParser\Header\Part\ContainerPart;
use ZBateson\MailMimeParser\Header\Part\NameValuePart;
use ZBateson\MailMimeParser\IMessage;
trait HasParsedMessage
{
/**
* The parsed message.
*/
protected ?IMessage $message = null;
/**
* Get all headers from the message.
*/
public function headers(): array
{
return $this->parse()->getAllHeaders();
}
/**
* Get a header from the message.
*/
public function header(string $name, int $offset = 0): ?IHeader
{
return $this->parse()->getHeader($name, $offset);
}
/**
* Get addresses from the given header.
*
* @return Address[]
*/
public function addresses(string $header): array
{
$parts = $this->header($header)?->getParts() ?? [];
$addresses = array_map(fn (IHeaderPart $part) => match (true) {
$part instanceof AddressPart => new Address($part->getEmail(), $part->getName()),
$part instanceof NameValuePart => new Address($part->getName(), $part->getValue()),
$part instanceof ContainerPart => new Address($part->getValue(), ''),
default => null,
}, $parts);
return array_filter($addresses);
}
/**
* Parse the message into a MailMimeMessage instance.
*/
public function parse(): IMessage
{
if ($this->isEmpty()) {
throw new RuntimeException('Cannot parse an empty message');
}
return $this->message ??= MessageParser::parse((string) $this);
}
/**
* Determine if the message is empty.
*/
abstract public function isEmpty(): bool;
/**
* Get the string representation of the message.
*/
abstract public function __toString(): string;
}

View File

@@ -0,0 +1,173 @@
<?php
namespace DirectoryTree\ImapEngine;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use Closure;
use DirectoryTree\ImapEngine\Connection\Responses\UntaggedResponse;
use DirectoryTree\ImapEngine\Connection\Tokens\Atom;
use DirectoryTree\ImapEngine\Exceptions\Exception;
use DirectoryTree\ImapEngine\Exceptions\ImapConnectionClosedException;
use DirectoryTree\ImapEngine\Exceptions\ImapConnectionTimedOutException;
use Generator;
class Idle
{
/**
* Constructor.
*/
public function __construct(
protected Mailbox $mailbox,
protected string $folder,
protected Closure|int $timeout,
) {}
/**
* Destructor.
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Await new messages on the connection.
*/
public function await(callable $callback): void
{
$this->connect();
while ($ttl = $this->getNextTimeout()) {
try {
$this->listen($callback, $ttl);
} catch (ImapConnectionTimedOutException) {
$this->restart();
} catch (ImapConnectionClosedException) {
$this->reconnect();
}
}
}
/**
* Start listening for new messages using the idle() generator.
*/
protected function listen(callable $callback, CarbonInterface $ttl): void
{
// Iterate over responses yielded by the idle generator.
foreach ($this->idle($ttl) as $response) {
if (! $response instanceof UntaggedResponse) {
continue;
}
if (! $token = $response->tokenAt(2)) {
continue;
}
if ($token instanceof Atom && $token->is('EXISTS')) {
$msgn = (int) $response->tokenAt(1)->value;
$callback($msgn);
$ttl = $this->getNextTimeout();
}
if ($ttl === false) {
break;
}
// If we've been idle too long, break out to restart the session.
if (Carbon::now()->greaterThanOrEqualTo($ttl)) {
$this->restart();
break;
}
}
}
/**
* Get the folder to idle.
*/
protected function folder(): FolderInterface
{
return $this->mailbox->folders()->findOrFail($this->folder);
}
/**
* Issue a done command and restart the idle session.
*/
protected function restart(): void
{
try {
// Send DONE to terminate the current IDLE session gracefully.
$this->done();
} catch (Exception) {
$this->reconnect();
}
}
/**
* Reconnect the client and restart the idle session.
*/
protected function reconnect(): void
{
$this->mailbox->disconnect();
$this->connect();
}
/**
* Connect the client and select the folder to idle.
*/
protected function connect(): void
{
$this->mailbox->connect();
$this->mailbox->select($this->folder(), true);
}
/**
* Disconnect the client.
*/
protected function disconnect(): void
{
try {
// Attempt to terminate IDLE gracefully.
$this->done();
} catch (Exception) {
// Do nothing.
}
$this->mailbox->disconnect();
}
/**
* End the current IDLE session.
*/
protected function done(): void
{
$this->mailbox->connection()->done();
}
/**
* Begin a new IDLE session as a generator.
*/
protected function idle(CarbonInterface $ttl): Generator
{
yield from $this->mailbox->connection()->idle(
(int) Carbon::now()->diffInSeconds($ttl, true)
);
}
/**
* Get the next timeout as a Carbon instance.
*/
protected function getNextTimeout(): CarbonInterface|false
{
if (is_numeric($seconds = value($this->timeout))) {
return Carbon::now()->addSeconds(abs($seconds));
}
return false;
}
}

View File

@@ -0,0 +1,232 @@
<?php
namespace DirectoryTree\ImapEngine;
use DirectoryTree\ImapEngine\Connection\ConnectionInterface;
use DirectoryTree\ImapEngine\Connection\ImapConnection;
use DirectoryTree\ImapEngine\Connection\Loggers\EchoLogger;
use DirectoryTree\ImapEngine\Connection\Loggers\FileLogger;
use DirectoryTree\ImapEngine\Connection\Streams\ImapStream;
use DirectoryTree\ImapEngine\Connection\Tokens\Token;
use Exception;
class Mailbox implements MailboxInterface
{
/**
* The mailbox configuration.
*/
protected array $config = [
'port' => 993,
'host' => '',
'timeout' => 30,
'debug' => false,
'username' => '',
'password' => '',
'encryption' => 'ssl',
'validate_cert' => true,
'authentication' => 'plain',
'proxy' => [
'socket' => null,
'username' => null,
'password' => null,
'request_fulluri' => false,
],
];
/**
* The cached mailbox capabilities.
*
* @see https://datatracker.ietf.org/doc/html/rfc9051#section-6.1.1
*/
protected ?array $capabilities = null;
/**
* The currently selected folder.
*/
protected ?FolderInterface $selected = null;
/**
* The mailbox connection.
*/
protected ?ConnectionInterface $connection = null;
/**
* Constructor.
*/
public function __construct(array $config = [])
{
$this->config = array_merge($this->config, $config);
}
/**
* Prepare the cloned instance.
*/
public function __clone(): void
{
$this->connection = null;
}
/**
* Make a new mailbox instance.
*/
public static function make(array $config = []): static
{
return new static($config);
}
/**
* {@inheritDoc}
*/
public function config(?string $key = null, mixed $default = null): mixed
{
if (is_null($key)) {
return $this->config;
}
return data_get($this->config, $key, $default);
}
/**
* {@inheritDoc}
*/
public function connection(): ConnectionInterface
{
if (! $this->connection) {
$this->connect();
}
return $this->connection;
}
/**
* {@inheritDoc}
*/
public function connected(): bool
{
return (bool) $this->connection?->connected();
}
/**
* {@inheritDoc}
*/
public function reconnect(): void
{
$this->disconnect();
$this->connect();
}
/**
* {@inheritDoc}
*/
public function connect(?ConnectionInterface $connection = null): void
{
if ($this->connected()) {
return;
}
$debug = $this->config('debug');
$this->connection = $connection ?? new ImapConnection(new ImapStream, match (true) {
class_exists($debug) => new $debug,
is_string($debug) => new FileLogger($debug),
is_bool($debug) && $debug => new EchoLogger,
default => null,
});
$this->connection->connect($this->config('host'), $this->config('port'), [
'proxy' => $this->config('proxy'),
'debug' => $this->config('debug'),
'timeout' => $this->config('timeout'),
'encryption' => $this->config('encryption'),
'validate_cert' => $this->config('validate_cert'),
]);
$this->authenticate();
}
/**
* Authenticate the current session.
*/
protected function authenticate(): void
{
if ($this->config('authentication') === 'oauth') {
$this->connection->authenticate(
$this->config('username'),
$this->config('password')
);
} else {
$this->connection->login(
$this->config('username'),
$this->config('password'),
);
}
}
/**
* {@inheritDoc}
*/
public function disconnect(): void
{
try {
$this->connection?->logout();
$this->connection?->disconnect();
} catch (Exception) {
// Do nothing.
} finally {
$this->connection = null;
}
}
/**
* {@inheritDoc}
*/
public function inbox(): FolderInterface
{
// "INBOX" is a special name reserved for the user's primary mailbox.
// See: https://datatracker.ietf.org/doc/html/rfc9051#section-5.1
return $this->folders()->find('INBOX');
}
/**
* {@inheritDoc}
*/
public function folders(): FolderRepositoryInterface
{
// Ensure the connection is established.
$this->connection();
return new FolderRepository($this);
}
/**
* {@inheritDoc}
*/
public function capabilities(): array
{
return $this->capabilities ??= array_map(
fn (Token $token) => $token->value,
$this->connection()->capability()->tokensAfter(2)
);
}
/**
* {@inheritDoc}
*/
public function select(FolderInterface $folder, bool $force = false): void
{
if (! $this->selected($folder) || $force) {
$this->connection()->select($folder->path());
}
$this->selected = $folder;
}
/**
* {@inheritDoc}
*/
public function selected(FolderInterface $folder): bool
{
return $this->selected?->is($folder) ?? false;
}
}

Some files were not shown because too many files have changed in this diff Show More