+
+ 1
+
+
+ 2
+
+
+XML;
+
+$doc = new DOMDocument;
+$doc->loadXML($xml);
+assertThat($doc, hasXPath("book", 2));
+```
+
diff --git a/vendor/hamcrest/hamcrest-php/composer.json b/vendor/hamcrest/hamcrest-php/composer.json
new file mode 100644
index 0000000..712ad96
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/composer.json
@@ -0,0 +1,37 @@
+{
+ "name": "hamcrest/hamcrest-php",
+ "type": "library",
+ "description": "This is the PHP port of Hamcrest Matchers",
+ "keywords": ["test"],
+ "license": "BSD-3-Clause",
+ "authors": [
+ ],
+
+ "autoload": {
+ "classmap": ["hamcrest"]
+ },
+ "autoload-dev": {
+ "classmap": ["tests", "generator"]
+ },
+
+ "require": {
+ "php": "^5.3|^7.0|^8.0"
+ },
+
+ "require-dev": {
+ "phpunit/php-file-iterator": "^1.4 || ^2.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
+ },
+
+ "replace": {
+ "kodova/hamcrest-php": "*",
+ "davedevelopment/hamcrest-php": "*",
+ "cordoval/hamcrest-php": "*"
+ },
+
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/FactoryCall.php b/vendor/hamcrest/hamcrest-php/generator/FactoryCall.php
new file mode 100644
index 0000000..83965b2
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/FactoryCall.php
@@ -0,0 +1,41 @@
+method = $method;
+ $this->name = $name;
+ }
+
+ public function getMethod()
+ {
+ return $this->method;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/FactoryClass.php b/vendor/hamcrest/hamcrest-php/generator/FactoryClass.php
new file mode 100644
index 0000000..a09cb73
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/FactoryClass.php
@@ -0,0 +1,71 @@
+file = $file;
+ $this->reflector = $class;
+ $this->extractFactoryMethods();
+ }
+
+ public function extractFactoryMethods()
+ {
+ $this->methods = array();
+ foreach ($this->getPublicStaticMethods() as $method) {
+ if ($method->isFactory()) {
+ $this->methods[] = $method;
+ }
+ }
+ }
+
+ public function getPublicStaticMethods()
+ {
+ $methods = array();
+ foreach ($this->reflector->getMethods(ReflectionMethod::IS_STATIC) as $method) {
+ if ($method->isPublic() && $method->getDeclaringClass() == $this->reflector) {
+ $methods[] = new FactoryMethod($this, $method);
+ }
+ }
+ return $methods;
+ }
+
+ public function getFile()
+ {
+ return $this->file;
+ }
+
+ public function getName()
+ {
+ return $this->reflector->name;
+ }
+
+ public function isFactory()
+ {
+ return !empty($this->methods);
+ }
+
+ public function getMethods()
+ {
+ return $this->methods;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/FactoryFile.php b/vendor/hamcrest/hamcrest-php/generator/FactoryFile.php
new file mode 100644
index 0000000..dd6109b
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/FactoryFile.php
@@ -0,0 +1,121 @@
+file = $file;
+ $this->indent = $indent;
+ }
+
+ abstract public function addCall(FactoryCall $call);
+
+ abstract public function build();
+
+ public function addFileHeader()
+ {
+ $this->code = '';
+ $this->addPart('file_header');
+ }
+
+ public function addPart($name)
+ {
+ $this->addCode($this->readPart($name));
+ }
+
+ public function addCode($code)
+ {
+ $this->code .= $code;
+ }
+
+ public function readPart($name)
+ {
+ return file_get_contents(__DIR__ . "/parts/$name.txt");
+ }
+
+ public function generateFactoryCall(FactoryCall $call)
+ {
+ $method = $call->getMethod();
+ $code = $method->getComment($this->indent) . "\n";
+ $code .= $this->generateDeclaration($call->getName(), $method);
+ $code .= $this->generateCall($method);
+ $code .= $this->generateClosing();
+ return $code;
+ }
+
+ public function generateDeclaration($name, FactoryMethod $method)
+ {
+ $code = $this->indent . $this->getDeclarationModifiers()
+ . 'function ' . $name . '('
+ . $this->generateDeclarationArguments($method)
+ . ')' . "\n" . $this->indent . '{' . "\n";
+ return $code;
+ }
+
+ public function getDeclarationModifiers()
+ {
+ return '';
+ }
+
+ public function generateDeclarationArguments(FactoryMethod $method)
+ {
+ if ($method->acceptsVariableArguments()) {
+ return '/* args... */';
+ } else {
+ return $method->getParameterDeclarations();
+ }
+ }
+
+ public function generateImport(FactoryMethod $method)
+ {
+ return $this->indent . self::INDENT . "require_once '" . $method->getClass()->getFile() . "';" . "\n";
+ }
+
+ public function generateCall(FactoryMethod $method)
+ {
+ $code = '';
+ if ($method->acceptsVariableArguments()) {
+ $code .= $this->indent . self::INDENT . '$args = func_get_args();' . "\n";
+ }
+
+ $code .= $this->indent . self::INDENT . 'return ';
+ if ($method->acceptsVariableArguments()) {
+ $code .= 'call_user_func_array(array(\''
+ . '\\' . $method->getClassName() . '\', \''
+ . $method->getName() . '\'), $args);' . "\n";
+ } else {
+ $code .= '\\' . $method->getClassName() . '::'
+ . $method->getName() . '('
+ . $method->getParameterInvocations() . ');' . "\n";
+ }
+
+ return $code;
+ }
+
+ public function generateClosing()
+ {
+ return $this->indent . '}' . "\n";
+ }
+
+ public function write()
+ {
+ file_put_contents($this->file, $this->code);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/FactoryGenerator.php b/vendor/hamcrest/hamcrest-php/generator/FactoryGenerator.php
new file mode 100644
index 0000000..242875a
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/FactoryGenerator.php
@@ -0,0 +1,124 @@
+path = $path;
+ $this->factoryFiles = array();
+ }
+
+ public function addFactoryFile(FactoryFile $factoryFile)
+ {
+ $this->factoryFiles[] = $factoryFile;
+ }
+
+ public function generate()
+ {
+ $classes = $this->getClassesWithFactoryMethods();
+ foreach ($classes as $class) {
+ foreach ($class->getMethods() as $method) {
+ foreach ($method->getCalls() as $call) {
+ foreach ($this->factoryFiles as $file) {
+ $file->addCall($call);
+ }
+ }
+ }
+ }
+ }
+
+ public function write()
+ {
+ foreach ($this->factoryFiles as $file) {
+ $file->build();
+ $file->write();
+ }
+ }
+
+ public function getClassesWithFactoryMethods()
+ {
+ $classes = array();
+ $files = $this->getSortedFiles();
+ foreach ($files as $file) {
+ $class = $this->getFactoryClass($file);
+ if ($class !== null) {
+ $classes[] = $class;
+ }
+ }
+
+ return $classes;
+ }
+
+ public function getSortedFiles()
+ {
+ $iter = $this->getFileIterator();
+ $files = array();
+ foreach ($iter as $file) {
+ $files[] = $file;
+ }
+ sort($files, SORT_STRING);
+
+ return $files;
+ }
+
+ private function getFileIterator()
+ {
+ $factoryClass = class_exists('File_Iterator_Factory') ? 'File_Iterator_Factory' : 'SebastianBergmann\FileIterator\Factory';
+
+ $factory = new $factoryClass();
+
+ return $factory->getFileIterator($this->path, '.php');
+ }
+
+ public function getFactoryClass($file)
+ {
+ $name = $this->getFactoryClassName($file);
+ if ($name !== null) {
+ require_once $file;
+
+ if (class_exists($name)) {
+ $class = new FactoryClass(substr($file, strpos($file, 'Hamcrest/')), new ReflectionClass($name));
+ if ($class->isFactory()) {
+ return $class;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public function getFactoryClassName($file)
+ {
+ $content = file_get_contents($file);
+ if (preg_match('/namespace\s+(.+);/', $content, $namespace)
+ && preg_match('/\n\s*class\s+(\w+)\s+extends\b/', $content, $className)
+ && preg_match('/@factory\b/', $content)
+ ) {
+ return $namespace[1] . '\\' . $className[1];
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/FactoryMethod.php b/vendor/hamcrest/hamcrest-php/generator/FactoryMethod.php
new file mode 100644
index 0000000..8a05371
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/FactoryMethod.php
@@ -0,0 +1,231 @@
+class = $class;
+ $this->reflector = $reflector;
+ $this->extractCommentWithoutLeadingShashesAndStars();
+ $this->extractFactoryNamesFromComment();
+ $this->extractParameters();
+ }
+
+ public function extractCommentWithoutLeadingShashesAndStars()
+ {
+ $this->comment = explode("\n", $this->reflector->getDocComment());
+ foreach ($this->comment as &$line) {
+ $line = preg_replace('#^\s*(/\\*+|\\*+/|\\*)\s?#', '', $line);
+ }
+ $this->trimLeadingBlankLinesFromComment();
+ $this->trimTrailingBlankLinesFromComment();
+ }
+
+ public function trimLeadingBlankLinesFromComment()
+ {
+ while (count($this->comment) > 0) {
+ $line = array_shift($this->comment);
+ if (trim($line) != '') {
+ array_unshift($this->comment, $line);
+ break;
+ }
+ }
+ }
+
+ public function trimTrailingBlankLinesFromComment()
+ {
+ while (count($this->comment) > 0) {
+ $line = array_pop($this->comment);
+ if (trim($line) != '') {
+ array_push($this->comment, $line);
+ break;
+ }
+ }
+ }
+
+ public function extractFactoryNamesFromComment()
+ {
+ $this->calls = array();
+ for ($i = 0; $i < count($this->comment); $i++) {
+ if ($this->extractFactoryNamesFromLine($this->comment[$i])) {
+ unset($this->comment[$i]);
+ }
+ }
+ $this->trimTrailingBlankLinesFromComment();
+ }
+
+ public function extractFactoryNamesFromLine($line)
+ {
+ if (preg_match('/^\s*@factory(\s+(.+))?$/', $line, $match)) {
+ $this->createCalls(
+ $this->extractFactoryNamesFromAnnotation(
+ isset($match[2]) ? trim($match[2]) : null
+ )
+ );
+ return true;
+ }
+ return false;
+ }
+
+ public function extractFactoryNamesFromAnnotation($value)
+ {
+ $primaryName = $this->reflector->getName();
+ if (empty($value)) {
+ return array($primaryName);
+ }
+ preg_match_all('/(\.{3}|-|[a-zA-Z_][a-zA-Z_0-9]*)/', $value, $match);
+ $names = $match[0];
+ if (in_array('...', $names)) {
+ $this->isVarArgs = true;
+ }
+ if (!in_array('-', $names) && !in_array($primaryName, $names)) {
+ array_unshift($names, $primaryName);
+ }
+ return $names;
+ }
+
+ public function createCalls(array $names)
+ {
+ $names = array_unique($names);
+ foreach ($names as $name) {
+ if ($name != '-' && $name != '...') {
+ $this->calls[] = new FactoryCall($this, $name);
+ }
+ }
+ }
+
+ public function extractParameters()
+ {
+ $this->parameters = array();
+ if (!$this->isVarArgs) {
+ foreach ($this->reflector->getParameters() as $parameter) {
+ $this->parameters[] = new FactoryParameter($this, $parameter);
+ }
+ }
+ }
+
+ public function getParameterDeclarations()
+ {
+ if ($this->isVarArgs || !$this->hasParameters()) {
+ return '';
+ }
+ $params = array();
+ foreach ($this->parameters as /** @var $parameter FactoryParameter */
+ $parameter) {
+ $params[] = $parameter->getDeclaration();
+ }
+ return implode(', ', $params);
+ }
+
+ public function getParameterInvocations()
+ {
+ if ($this->isVarArgs) {
+ return '';
+ }
+ $params = array();
+ foreach ($this->parameters as $parameter) {
+ $params[] = $parameter->getInvocation();
+ }
+ return implode(', ', $params);
+ }
+
+
+ public function getClass()
+ {
+ return $this->class;
+ }
+
+ public function getClassName()
+ {
+ return $this->class->getName();
+ }
+
+ public function getName()
+ {
+ return $this->reflector->name;
+ }
+
+ public function isFactory()
+ {
+ return count($this->calls) > 0;
+ }
+
+ public function getCalls()
+ {
+ return $this->calls;
+ }
+
+ public function acceptsVariableArguments()
+ {
+ return $this->isVarArgs;
+ }
+
+ public function hasParameters()
+ {
+ return !empty($this->parameters);
+ }
+
+ public function getParameters()
+ {
+ return $this->parameters;
+ }
+
+ public function getFullName()
+ {
+ return $this->getClassName() . '::' . $this->getName();
+ }
+
+ public function getCommentText()
+ {
+ return implode("\n", $this->comment);
+ }
+
+ public function getComment($indent = '')
+ {
+ $comment = $indent . '/**';
+ foreach ($this->comment as $line) {
+ $comment .= "\n" . rtrim($indent . ' * ' . $line);
+ }
+ $comment .= "\n" . $indent . ' */';
+ return $comment;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/FactoryParameter.php b/vendor/hamcrest/hamcrest-php/generator/FactoryParameter.php
new file mode 100644
index 0000000..82b707a
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/FactoryParameter.php
@@ -0,0 +1,131 @@
+method = $method;
+ $this->reflector = $reflector;
+ }
+
+ /**
+ * Compute the declaration code.
+ *
+ * @return string
+ */
+ public function getDeclaration()
+ {
+ $code = $this->getTypeCode() . $this->getInvocation();
+
+ if ($this->reflector->isOptional()) {
+ $default = $this->reflector->getDefaultValue();
+ if (is_null($default)) {
+ $default = 'null';
+ } elseif (is_bool($default)) {
+ $default = $default ? 'true' : 'false';
+ } elseif (is_string($default)) {
+ $default = "'" . $default . "'";
+ } elseif (is_numeric($default)) {
+ $default = strval($default);
+ } elseif (is_array($default)) {
+ $default = 'array()';
+ } else {
+ echo 'Warning: unknown default type for ' . $this->getMethod()->getFullName() . "\n";
+ var_dump($default);
+ $default = 'null';
+ }
+ $code .= ' = ' . $default;
+ }
+ return $code;
+ }
+
+ /**
+ * Compute the type code for the paramater.
+ *
+ * @return string
+ */
+ private function getTypeCode()
+ {
+ // Handle PHP 5 separately
+ if (PHP_VERSION_ID < 70000) {
+ if ($this->reflector->isArray()) {
+ return 'array';
+ }
+
+ $class = $this->reflector->getClass();
+
+ return $class ? sprintf('\\%s ', $class->getName()) : '';
+ }
+
+ if (!$this->reflector->hasType()) {
+ return '';
+ }
+
+ $type = $this->reflector->getType();
+ $name = self::getQualifiedName($type);
+
+ // PHP 7.1+ supports nullable types via a leading question mark
+ return (PHP_VERSION_ID >= 70100 && $type->allowsNull()) ? sprintf('?%s ', $name) : sprintf('%s ', $name);
+ }
+
+ /**
+ * Compute qualified name for the given type.
+ *
+ * This function knows how to prefix class names with a leading slash and
+ * also how to handle PHP 8's union types.
+ *
+ * @param ReflectionType $type
+ *
+ * @return string
+ */
+ private static function getQualifiedName(ReflectionType $type)
+ {
+ // PHP 8 union types can be recursively processed
+ if ($type instanceof ReflectionUnionType) {
+ return implode('|', array_map(function (ReflectionType $type) {
+ // The "self::" call within a Closure is fine here because this
+ // code will only ever be executed on PHP 7.0+
+ return self::getQualifiedName($type);
+ }, $type->getTypes()));
+ }
+
+ // PHP 7.0 doesn't have named types, but 7.1+ does
+ $name = $type instanceof ReflectionNamedType ? $type->getName() : (string) $type;
+
+ return $type->isBuiltin() ? $name : sprintf('\\%s', $name);
+ }
+
+ /**
+ * Compute the invocation code.
+ *
+ * @return string
+ */
+ public function getInvocation()
+ {
+ return sprintf('$%s', $this->reflector->getName());
+ }
+
+ /**
+ * Compute the method name.
+ *
+ * @return string
+ */
+ public function getMethod()
+ {
+ return $this->method;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/GlobalFunctionFile.php b/vendor/hamcrest/hamcrest-php/generator/GlobalFunctionFile.php
new file mode 100644
index 0000000..ec8b1b3
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/GlobalFunctionFile.php
@@ -0,0 +1,42 @@
+functions = '';
+ }
+
+ public function addCall(FactoryCall $call)
+ {
+ $this->functions .= "\n" . $this->generateFactoryCall($call);
+ }
+
+ public function build()
+ {
+ $this->addFileHeader();
+ $this->addPart('functions_imports');
+ $this->addPart('functions_header');
+ $this->addCode($this->functions);
+ $this->addPart('functions_footer');
+ }
+
+ public function generateFactoryCall(FactoryCall $call)
+ {
+ $code = "if (!function_exists('{$call->getName()}')) {\n";
+ $code.= parent::generateFactoryCall($call);
+ $code.= "}\n";
+
+ return $code;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/StaticMethodFile.php b/vendor/hamcrest/hamcrest-php/generator/StaticMethodFile.php
new file mode 100644
index 0000000..44cec02
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/StaticMethodFile.php
@@ -0,0 +1,38 @@
+methods = '';
+ }
+
+ public function addCall(FactoryCall $call)
+ {
+ $this->methods .= PHP_EOL . $this->generateFactoryCall($call);
+ }
+
+ public function getDeclarationModifiers()
+ {
+ return 'public static ';
+ }
+
+ public function build()
+ {
+ $this->addFileHeader();
+ $this->addPart('matchers_imports');
+ $this->addPart('matchers_header');
+ $this->addCode($this->methods);
+ $this->addPart('matchers_footer');
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/parts/file_header.txt b/vendor/hamcrest/hamcrest-php/generator/parts/file_header.txt
new file mode 100644
index 0000000..7b352e4
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/parts/file_header.txt
@@ -0,0 +1,7 @@
+
+ * //With an identifier
+ * assertThat("assertion identifier", $apple->flavour(), equalTo("tasty"));
+ * //Without an identifier
+ * assertThat($apple->flavour(), equalTo("tasty"));
+ * //Evaluating a boolean expression
+ * assertThat("some error", $a > $b);
+ *
+ */
+ function assertThat()
+ {
+ $args = func_get_args();
+ call_user_func_array(
+ array('Hamcrest\MatcherAssert', 'assertThat'),
+ $args
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/parts/functions_imports.txt b/vendor/hamcrest/hamcrest-php/generator/parts/functions_imports.txt
new file mode 100644
index 0000000..e69de29
diff --git a/vendor/hamcrest/hamcrest-php/generator/parts/matchers_footer.txt b/vendor/hamcrest/hamcrest-php/generator/parts/matchers_footer.txt
new file mode 100644
index 0000000..5c34318
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/parts/matchers_footer.txt
@@ -0,0 +1 @@
+}
diff --git a/vendor/hamcrest/hamcrest-php/generator/parts/matchers_header.txt b/vendor/hamcrest/hamcrest-php/generator/parts/matchers_header.txt
new file mode 100644
index 0000000..4f8bb2b
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/parts/matchers_header.txt
@@ -0,0 +1,7 @@
+
+
+/**
+ * A series of static factories for all hamcrest matchers.
+ */
+class Matchers
+{
diff --git a/vendor/hamcrest/hamcrest-php/generator/parts/matchers_imports.txt b/vendor/hamcrest/hamcrest-php/generator/parts/matchers_imports.txt
new file mode 100644
index 0000000..7dd6849
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/parts/matchers_imports.txt
@@ -0,0 +1,2 @@
+
+namespace Hamcrest;
\ No newline at end of file
diff --git a/vendor/hamcrest/hamcrest-php/generator/run.php b/vendor/hamcrest/hamcrest-php/generator/run.php
new file mode 100644
index 0000000..924d752
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/generator/run.php
@@ -0,0 +1,37 @@
+addFactoryFile(new StaticMethodFile(STATIC_MATCHERS_FILE));
+$generator->addFactoryFile(new GlobalFunctionFile(GLOBAL_FUNCTIONS_FILE));
+$generator->generate();
+$generator->write();
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php
new file mode 100644
index 0000000..55a2dd8
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php
@@ -0,0 +1,882 @@
+
+ * //With an identifier
+ * assertThat("assertion identifier", $apple->flavour(), equalTo("tasty"));
+ * //Without an identifier
+ * assertThat($apple->flavour(), equalTo("tasty"));
+ * //Evaluating a boolean expression
+ * assertThat("some error", $a > $b);
+ *
+ */
+ function assertThat()
+ {
+ $args = func_get_args();
+ call_user_func_array(
+ array('Hamcrest\MatcherAssert', 'assertThat'),
+ $args
+ );
+ }
+}
+
+if (!function_exists('anArray')) {
+ /**
+ * Evaluates to true only if each $matcher[$i] is satisfied by $array[$i].
+ */
+ function anArray(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Arrays\IsArray', 'anArray'), $args);
+ }
+}
+
+if (!function_exists('hasItemInArray')) {
+ /**
+ * Evaluates to true if any item in an array satisfies the given matcher.
+ *
+ * @param mixed $item as a {@link Hamcrest\Matcher} or a value.
+ *
+ * @return \Hamcrest\Arrays\IsArrayContaining
+ */
+ function hasItemInArray($item)
+ {
+ return \Hamcrest\Arrays\IsArrayContaining::hasItemInArray($item);
+ }
+}
+
+if (!function_exists('hasValue')) {
+ /**
+ * Evaluates to true if any item in an array satisfies the given matcher.
+ *
+ * @param mixed $item as a {@link Hamcrest\Matcher} or a value.
+ *
+ * @return \Hamcrest\Arrays\IsArrayContaining
+ */
+ function hasValue($item)
+ {
+ return \Hamcrest\Arrays\IsArrayContaining::hasItemInArray($item);
+ }
+}
+
+if (!function_exists('arrayContainingInAnyOrder')) {
+ /**
+ * An array with elements that match the given matchers.
+ */
+ function arrayContainingInAnyOrder(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Arrays\IsArrayContainingInAnyOrder', 'arrayContainingInAnyOrder'), $args);
+ }
+}
+
+if (!function_exists('containsInAnyOrder')) {
+ /**
+ * An array with elements that match the given matchers.
+ */
+ function containsInAnyOrder(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Arrays\IsArrayContainingInAnyOrder', 'arrayContainingInAnyOrder'), $args);
+ }
+}
+
+if (!function_exists('arrayContaining')) {
+ /**
+ * An array with elements that match the given matchers in the same order.
+ */
+ function arrayContaining(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Arrays\IsArrayContainingInOrder', 'arrayContaining'), $args);
+ }
+}
+
+if (!function_exists('contains')) {
+ /**
+ * An array with elements that match the given matchers in the same order.
+ */
+ function contains(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Arrays\IsArrayContainingInOrder', 'arrayContaining'), $args);
+ }
+}
+
+if (!function_exists('hasKeyInArray')) {
+ /**
+ * Evaluates to true if any key in an array matches the given matcher.
+ *
+ * @param mixed $key as a {@link Hamcrest\Matcher} or a value.
+ *
+ * @return \Hamcrest\Arrays\IsArrayContainingKey
+ */
+ function hasKeyInArray($key)
+ {
+ return \Hamcrest\Arrays\IsArrayContainingKey::hasKeyInArray($key);
+ }
+}
+
+if (!function_exists('hasKey')) {
+ /**
+ * Evaluates to true if any key in an array matches the given matcher.
+ *
+ * @param mixed $key as a {@link Hamcrest\Matcher} or a value.
+ *
+ * @return \Hamcrest\Arrays\IsArrayContainingKey
+ */
+ function hasKey($key)
+ {
+ return \Hamcrest\Arrays\IsArrayContainingKey::hasKeyInArray($key);
+ }
+}
+
+if (!function_exists('hasKeyValuePair')) {
+ /**
+ * Test if an array has both an key and value in parity with each other.
+ */
+ function hasKeyValuePair($key, $value)
+ {
+ return \Hamcrest\Arrays\IsArrayContainingKeyValuePair::hasKeyValuePair($key, $value);
+ }
+}
+
+if (!function_exists('hasEntry')) {
+ /**
+ * Test if an array has both an key and value in parity with each other.
+ */
+ function hasEntry($key, $value)
+ {
+ return \Hamcrest\Arrays\IsArrayContainingKeyValuePair::hasKeyValuePair($key, $value);
+ }
+}
+
+if (!function_exists('arrayWithSize')) {
+ /**
+ * Does array size satisfy a given matcher?
+ *
+ * @param \Hamcrest\Matcher|int $size as a {@link Hamcrest\Matcher} or a value.
+ *
+ * @return \Hamcrest\Arrays\IsArrayWithSize
+ */
+ function arrayWithSize($size)
+ {
+ return \Hamcrest\Arrays\IsArrayWithSize::arrayWithSize($size);
+ }
+}
+
+if (!function_exists('emptyArray')) {
+ /**
+ * Matches an empty array.
+ */
+ function emptyArray()
+ {
+ return \Hamcrest\Arrays\IsArrayWithSize::emptyArray();
+ }
+}
+
+if (!function_exists('nonEmptyArray')) {
+ /**
+ * Matches an empty array.
+ */
+ function nonEmptyArray()
+ {
+ return \Hamcrest\Arrays\IsArrayWithSize::nonEmptyArray();
+ }
+}
+
+if (!function_exists('emptyTraversable')) {
+ /**
+ * Returns true if traversable is empty.
+ */
+ function emptyTraversable()
+ {
+ return \Hamcrest\Collection\IsEmptyTraversable::emptyTraversable();
+ }
+}
+
+if (!function_exists('nonEmptyTraversable')) {
+ /**
+ * Returns true if traversable is not empty.
+ */
+ function nonEmptyTraversable()
+ {
+ return \Hamcrest\Collection\IsEmptyTraversable::nonEmptyTraversable();
+ }
+}
+
+if (!function_exists('traversableWithSize')) {
+ /**
+ * Does traversable size satisfy a given matcher?
+ */
+ function traversableWithSize($size)
+ {
+ return \Hamcrest\Collection\IsTraversableWithSize::traversableWithSize($size);
+ }
+}
+
+if (!function_exists('allOf')) {
+ /**
+ * Evaluates to true only if ALL of the passed in matchers evaluate to true.
+ */
+ function allOf(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Core\AllOf', 'allOf'), $args);
+ }
+}
+
+if (!function_exists('anyOf')) {
+ /**
+ * Evaluates to true if ANY of the passed in matchers evaluate to true.
+ */
+ function anyOf(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Core\AnyOf', 'anyOf'), $args);
+ }
+}
+
+if (!function_exists('noneOf')) {
+ /**
+ * Evaluates to false if ANY of the passed in matchers evaluate to true.
+ */
+ function noneOf(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Core\AnyOf', 'noneOf'), $args);
+ }
+}
+
+if (!function_exists('both')) {
+ /**
+ * This is useful for fluently combining matchers that must both pass.
+ * For example:
+ *
+ * assertThat($string, both(containsString("a"))->andAlso(containsString("b")));
+ *
+ */
+ function both(\Hamcrest\Matcher $matcher)
+ {
+ return \Hamcrest\Core\CombinableMatcher::both($matcher);
+ }
+}
+
+if (!function_exists('either')) {
+ /**
+ * This is useful for fluently combining matchers where either may pass,
+ * for example:
+ *
+ * assertThat($string, either(containsString("a"))->orElse(containsString("b")));
+ *
+ */
+ function either(\Hamcrest\Matcher $matcher)
+ {
+ return \Hamcrest\Core\CombinableMatcher::either($matcher);
+ }
+}
+
+if (!function_exists('describedAs')) {
+ /**
+ * Wraps an existing matcher and overrides the description when it fails.
+ */
+ function describedAs(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Core\DescribedAs', 'describedAs'), $args);
+ }
+}
+
+if (!function_exists('everyItem')) {
+ /**
+ * @param Matcher $itemMatcher
+ * A matcher to apply to every element in an array.
+ *
+ * @return \Hamcrest\Core\Every
+ * Evaluates to TRUE for a collection in which every item matches $itemMatcher
+ */
+ function everyItem(\Hamcrest\Matcher $itemMatcher)
+ {
+ return \Hamcrest\Core\Every::everyItem($itemMatcher);
+ }
+}
+
+if (!function_exists('hasToString')) {
+ /**
+ * Does array size satisfy a given matcher?
+ */
+ function hasToString($matcher)
+ {
+ return \Hamcrest\Core\HasToString::hasToString($matcher);
+ }
+}
+
+if (!function_exists('is')) {
+ /**
+ * Decorates another Matcher, retaining the behavior but allowing tests
+ * to be slightly more expressive.
+ *
+ * For example: assertThat($cheese, equalTo($smelly))
+ * vs. assertThat($cheese, is(equalTo($smelly)))
+ */
+ function is($value)
+ {
+ return \Hamcrest\Core\Is::is($value);
+ }
+}
+
+if (!function_exists('anything')) {
+ /**
+ * This matcher always evaluates to true.
+ *
+ * @param string $description A meaningful string used when describing itself.
+ *
+ * @return \Hamcrest\Core\IsAnything
+ */
+ function anything($description = 'ANYTHING')
+ {
+ return \Hamcrest\Core\IsAnything::anything($description);
+ }
+}
+
+if (!function_exists('hasItem')) {
+ /**
+ * Test if the value is an array containing this matcher.
+ *
+ * Example:
+ *
+ * assertThat(array('a', 'b'), hasItem(equalTo('b')));
+ * //Convenience defaults to equalTo()
+ * assertThat(array('a', 'b'), hasItem('b'));
+ *
+ */
+ function hasItem(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Core\IsCollectionContaining', 'hasItem'), $args);
+ }
+}
+
+if (!function_exists('hasItems')) {
+ /**
+ * Test if the value is an array containing elements that match all of these
+ * matchers.
+ *
+ * Example:
+ *
+ * assertThat(array('a', 'b', 'c'), hasItems(equalTo('a'), equalTo('b')));
+ *
+ */
+ function hasItems(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Core\IsCollectionContaining', 'hasItems'), $args);
+ }
+}
+
+if (!function_exists('equalTo')) {
+ /**
+ * Is the value equal to another value, as tested by the use of the "=="
+ * comparison operator?
+ */
+ function equalTo($item)
+ {
+ return \Hamcrest\Core\IsEqual::equalTo($item);
+ }
+}
+
+if (!function_exists('identicalTo')) {
+ /**
+ * Tests of the value is identical to $value as tested by the "===" operator.
+ */
+ function identicalTo($value)
+ {
+ return \Hamcrest\Core\IsIdentical::identicalTo($value);
+ }
+}
+
+if (!function_exists('anInstanceOf')) {
+ /**
+ * Is the value an instance of a particular type?
+ * This version assumes no relationship between the required type and
+ * the signature of the method that sets it up, for example in
+ * assertThat($anObject, anInstanceOf('Thing'));
+ */
+ function anInstanceOf($theClass)
+ {
+ return \Hamcrest\Core\IsInstanceOf::anInstanceOf($theClass);
+ }
+}
+
+if (!function_exists('any')) {
+ /**
+ * Is the value an instance of a particular type?
+ * This version assumes no relationship between the required type and
+ * the signature of the method that sets it up, for example in
+ * assertThat($anObject, anInstanceOf('Thing'));
+ */
+ function any($theClass)
+ {
+ return \Hamcrest\Core\IsInstanceOf::anInstanceOf($theClass);
+ }
+}
+
+if (!function_exists('not')) {
+ /**
+ * Matches if value does not match $value.
+ */
+ function not($value)
+ {
+ return \Hamcrest\Core\IsNot::not($value);
+ }
+}
+
+if (!function_exists('nullValue')) {
+ /**
+ * Matches if value is null.
+ */
+ function nullValue()
+ {
+ return \Hamcrest\Core\IsNull::nullValue();
+ }
+}
+
+if (!function_exists('notNullValue')) {
+ /**
+ * Matches if value is not null.
+ */
+ function notNullValue()
+ {
+ return \Hamcrest\Core\IsNull::notNullValue();
+ }
+}
+
+if (!function_exists('sameInstance')) {
+ /**
+ * Creates a new instance of IsSame.
+ *
+ * @param mixed $object
+ * The predicate evaluates to true only when the argument is
+ * this object.
+ *
+ * @return \Hamcrest\Core\IsSame
+ */
+ function sameInstance($object)
+ {
+ return \Hamcrest\Core\IsSame::sameInstance($object);
+ }
+}
+
+if (!function_exists('typeOf')) {
+ /**
+ * Is the value a particular built-in type?
+ */
+ function typeOf($theType)
+ {
+ return \Hamcrest\Core\IsTypeOf::typeOf($theType);
+ }
+}
+
+if (!function_exists('set')) {
+ /**
+ * Matches if value (class, object, or array) has named $property.
+ */
+ function set($property)
+ {
+ return \Hamcrest\Core\Set::set($property);
+ }
+}
+
+if (!function_exists('notSet')) {
+ /**
+ * Matches if value (class, object, or array) does not have named $property.
+ */
+ function notSet($property)
+ {
+ return \Hamcrest\Core\Set::notSet($property);
+ }
+}
+
+if (!function_exists('closeTo')) {
+ /**
+ * Matches if value is a number equal to $value within some range of
+ * acceptable error $delta.
+ */
+ function closeTo($value, $delta)
+ {
+ return \Hamcrest\Number\IsCloseTo::closeTo($value, $delta);
+ }
+}
+
+if (!function_exists('comparesEqualTo')) {
+ /**
+ * The value is not > $value, nor < $value.
+ */
+ function comparesEqualTo($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::comparesEqualTo($value);
+ }
+}
+
+if (!function_exists('greaterThan')) {
+ /**
+ * The value is > $value.
+ */
+ function greaterThan($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::greaterThan($value);
+ }
+}
+
+if (!function_exists('greaterThanOrEqualTo')) {
+ /**
+ * The value is >= $value.
+ */
+ function greaterThanOrEqualTo($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::greaterThanOrEqualTo($value);
+ }
+}
+
+if (!function_exists('atLeast')) {
+ /**
+ * The value is >= $value.
+ */
+ function atLeast($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::greaterThanOrEqualTo($value);
+ }
+}
+
+if (!function_exists('lessThan')) {
+ /**
+ * The value is < $value.
+ */
+ function lessThan($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::lessThan($value);
+ }
+}
+
+if (!function_exists('lessThanOrEqualTo')) {
+ /**
+ * The value is <= $value.
+ */
+ function lessThanOrEqualTo($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::lessThanOrEqualTo($value);
+ }
+}
+
+if (!function_exists('atMost')) {
+ /**
+ * The value is <= $value.
+ */
+ function atMost($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::lessThanOrEqualTo($value);
+ }
+}
+
+if (!function_exists('isEmptyString')) {
+ /**
+ * Matches if value is a zero-length string.
+ */
+ function isEmptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isEmptyString();
+ }
+}
+
+if (!function_exists('emptyString')) {
+ /**
+ * Matches if value is a zero-length string.
+ */
+ function emptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isEmptyString();
+ }
+}
+
+if (!function_exists('isEmptyOrNullString')) {
+ /**
+ * Matches if value is null or a zero-length string.
+ */
+ function isEmptyOrNullString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isEmptyOrNullString();
+ }
+}
+
+if (!function_exists('nullOrEmptyString')) {
+ /**
+ * Matches if value is null or a zero-length string.
+ */
+ function nullOrEmptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isEmptyOrNullString();
+ }
+}
+
+if (!function_exists('isNonEmptyString')) {
+ /**
+ * Matches if value is a non-zero-length string.
+ */
+ function isNonEmptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isNonEmptyString();
+ }
+}
+
+if (!function_exists('nonEmptyString')) {
+ /**
+ * Matches if value is a non-zero-length string.
+ */
+ function nonEmptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isNonEmptyString();
+ }
+}
+
+if (!function_exists('equalToIgnoringCase')) {
+ /**
+ * Matches if value is a string equal to $string, regardless of the case.
+ */
+ function equalToIgnoringCase($string)
+ {
+ return \Hamcrest\Text\IsEqualIgnoringCase::equalToIgnoringCase($string);
+ }
+}
+
+if (!function_exists('equalToIgnoringWhiteSpace')) {
+ /**
+ * Matches if value is a string equal to $string, regardless of whitespace.
+ */
+ function equalToIgnoringWhiteSpace($string)
+ {
+ return \Hamcrest\Text\IsEqualIgnoringWhiteSpace::equalToIgnoringWhiteSpace($string);
+ }
+}
+
+if (!function_exists('matchesPattern')) {
+ /**
+ * Matches if value is a string that matches regular expression $pattern.
+ */
+ function matchesPattern($pattern)
+ {
+ return \Hamcrest\Text\MatchesPattern::matchesPattern($pattern);
+ }
+}
+
+if (!function_exists('containsString')) {
+ /**
+ * Matches if value is a string that contains $substring.
+ */
+ function containsString($substring)
+ {
+ return \Hamcrest\Text\StringContains::containsString($substring);
+ }
+}
+
+if (!function_exists('containsStringIgnoringCase')) {
+ /**
+ * Matches if value is a string that contains $substring regardless of the case.
+ */
+ function containsStringIgnoringCase($substring)
+ {
+ return \Hamcrest\Text\StringContainsIgnoringCase::containsStringIgnoringCase($substring);
+ }
+}
+
+if (!function_exists('stringContainsInOrder')) {
+ /**
+ * Matches if value contains $substrings in a constrained order.
+ */
+ function stringContainsInOrder(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Text\StringContainsInOrder', 'stringContainsInOrder'), $args);
+ }
+}
+
+if (!function_exists('endsWith')) {
+ /**
+ * Matches if value is a string that ends with $substring.
+ */
+ function endsWith($substring)
+ {
+ return \Hamcrest\Text\StringEndsWith::endsWith($substring);
+ }
+}
+
+if (!function_exists('startsWith')) {
+ /**
+ * Matches if value is a string that starts with $substring.
+ */
+ function startsWith($substring)
+ {
+ return \Hamcrest\Text\StringStartsWith::startsWith($substring);
+ }
+}
+
+if (!function_exists('arrayValue')) {
+ /**
+ * Is the value an array?
+ */
+ function arrayValue()
+ {
+ return \Hamcrest\Type\IsArray::arrayValue();
+ }
+}
+
+if (!function_exists('booleanValue')) {
+ /**
+ * Is the value a boolean?
+ */
+ function booleanValue()
+ {
+ return \Hamcrest\Type\IsBoolean::booleanValue();
+ }
+}
+
+if (!function_exists('boolValue')) {
+ /**
+ * Is the value a boolean?
+ */
+ function boolValue()
+ {
+ return \Hamcrest\Type\IsBoolean::booleanValue();
+ }
+}
+
+if (!function_exists('callableValue')) {
+ /**
+ * Is the value callable?
+ */
+ function callableValue()
+ {
+ return \Hamcrest\Type\IsCallable::callableValue();
+ }
+}
+
+if (!function_exists('doubleValue')) {
+ /**
+ * Is the value a float/double?
+ */
+ function doubleValue()
+ {
+ return \Hamcrest\Type\IsDouble::doubleValue();
+ }
+}
+
+if (!function_exists('floatValue')) {
+ /**
+ * Is the value a float/double?
+ */
+ function floatValue()
+ {
+ return \Hamcrest\Type\IsDouble::doubleValue();
+ }
+}
+
+if (!function_exists('integerValue')) {
+ /**
+ * Is the value an integer?
+ */
+ function integerValue()
+ {
+ return \Hamcrest\Type\IsInteger::integerValue();
+ }
+}
+
+if (!function_exists('intValue')) {
+ /**
+ * Is the value an integer?
+ */
+ function intValue()
+ {
+ return \Hamcrest\Type\IsInteger::integerValue();
+ }
+}
+
+if (!function_exists('numericValue')) {
+ /**
+ * Is the value a numeric?
+ */
+ function numericValue()
+ {
+ return \Hamcrest\Type\IsNumeric::numericValue();
+ }
+}
+
+if (!function_exists('objectValue')) {
+ /**
+ * Is the value an object?
+ */
+ function objectValue()
+ {
+ return \Hamcrest\Type\IsObject::objectValue();
+ }
+}
+
+if (!function_exists('anObject')) {
+ /**
+ * Is the value an object?
+ */
+ function anObject()
+ {
+ return \Hamcrest\Type\IsObject::objectValue();
+ }
+}
+
+if (!function_exists('resourceValue')) {
+ /**
+ * Is the value a resource?
+ */
+ function resourceValue()
+ {
+ return \Hamcrest\Type\IsResource::resourceValue();
+ }
+}
+
+if (!function_exists('scalarValue')) {
+ /**
+ * Is the value a scalar (boolean, integer, double, or string)?
+ */
+ function scalarValue()
+ {
+ return \Hamcrest\Type\IsScalar::scalarValue();
+ }
+}
+
+if (!function_exists('stringValue')) {
+ /**
+ * Is the value a string?
+ */
+ function stringValue()
+ {
+ return \Hamcrest\Type\IsString::stringValue();
+ }
+}
+
+if (!function_exists('hasXPath')) {
+ /**
+ * Wraps $matcher
with {@link Hamcrest\Core\IsEqual)
+ * if it's not a matcher and the XPath in count()
+ * if it's an integer.
+ */
+ function hasXPath($xpath, $matcher = null)
+ {
+ return \Hamcrest\Xml\HasXPath::hasXPath($xpath, $matcher);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArray.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArray.php
new file mode 100644
index 0000000..9ea5697
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArray.php
@@ -0,0 +1,118 @@
+_elementMatchers = $elementMatchers;
+ }
+
+ protected function matchesSafely($array)
+ {
+ if (array_keys($array) != array_keys($this->_elementMatchers)) {
+ return false;
+ }
+
+ /** @var $matcher \Hamcrest\Matcher */
+ foreach ($this->_elementMatchers as $k => $matcher) {
+ if (!$matcher->matches($array[$k])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected function describeMismatchSafely($actual, Description $mismatchDescription)
+ {
+ if (count($actual) != count($this->_elementMatchers)) {
+ $mismatchDescription->appendText('array length was ' . count($actual));
+
+ return;
+ } elseif (array_keys($actual) != array_keys($this->_elementMatchers)) {
+ $mismatchDescription->appendText('array keys were ')
+ ->appendValueList(
+ $this->descriptionStart(),
+ $this->descriptionSeparator(),
+ $this->descriptionEnd(),
+ array_keys($actual)
+ )
+ ;
+
+ return;
+ }
+
+ /** @var $matcher \Hamcrest\Matcher */
+ foreach ($this->_elementMatchers as $k => $matcher) {
+ if (!$matcher->matches($actual[$k])) {
+ $mismatchDescription->appendText('element ')->appendValue($k)
+ ->appendText(' was ')->appendValue($actual[$k]);
+
+ return;
+ }
+ }
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendList(
+ $this->descriptionStart(),
+ $this->descriptionSeparator(),
+ $this->descriptionEnd(),
+ $this->_elementMatchers
+ );
+ }
+
+ /**
+ * Evaluates to true only if each $matcher[$i] is satisfied by $array[$i].
+ *
+ * @factory ...
+ */
+ public static function anArray(/* args... */)
+ {
+ $args = func_get_args();
+
+ return new self(Util::createMatcherArray($args));
+ }
+
+ // -- Protected Methods
+
+ protected function descriptionStart()
+ {
+ return '[';
+ }
+
+ protected function descriptionSeparator()
+ {
+ return ', ';
+ }
+
+ protected function descriptionEnd()
+ {
+ return ']';
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContaining.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContaining.php
new file mode 100644
index 0000000..0e4a1ed
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContaining.php
@@ -0,0 +1,63 @@
+_elementMatcher = $elementMatcher;
+ }
+
+ protected function matchesSafely($array)
+ {
+ foreach ($array as $element) {
+ if ($this->_elementMatcher->matches($element)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function describeMismatchSafely($array, Description $mismatchDescription)
+ {
+ $mismatchDescription->appendText('was ')->appendValue($array);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description
+ ->appendText('an array containing ')
+ ->appendDescriptionOf($this->_elementMatcher)
+ ;
+ }
+
+ /**
+ * Evaluates to true if any item in an array satisfies the given matcher.
+ *
+ * @param mixed $item as a {@link Hamcrest\Matcher} or a value.
+ *
+ * @return \Hamcrest\Arrays\IsArrayContaining
+ * @factory hasValue
+ */
+ public static function hasItemInArray($item)
+ {
+ return new self(Util::wrapValueWithIsEqual($item));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingInAnyOrder.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingInAnyOrder.php
new file mode 100644
index 0000000..9009026
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingInAnyOrder.php
@@ -0,0 +1,59 @@
+_elementMatchers = $elementMatchers;
+ }
+
+ protected function matchesSafelyWithDiagnosticDescription($array, Description $mismatchDescription)
+ {
+ $matching = new MatchingOnce($this->_elementMatchers, $mismatchDescription);
+
+ foreach ($array as $element) {
+ if (!$matching->matches($element)) {
+ return false;
+ }
+ }
+
+ return $matching->isFinished($array);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendList('[', ', ', ']', $this->_elementMatchers)
+ ->appendText(' in any order')
+ ;
+ }
+
+ /**
+ * An array with elements that match the given matchers.
+ *
+ * @factory containsInAnyOrder ...
+ */
+ public static function arrayContainingInAnyOrder(/* args... */)
+ {
+ $args = func_get_args();
+
+ return new self(Util::createMatcherArray($args));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingInOrder.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingInOrder.php
new file mode 100644
index 0000000..6115740
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingInOrder.php
@@ -0,0 +1,57 @@
+_elementMatchers = $elementMatchers;
+ }
+
+ protected function matchesSafelyWithDiagnosticDescription($array, Description $mismatchDescription)
+ {
+ $series = new SeriesMatchingOnce($this->_elementMatchers, $mismatchDescription);
+
+ foreach ($array as $element) {
+ if (!$series->matches($element)) {
+ return false;
+ }
+ }
+
+ return $series->isFinished();
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendList('[', ', ', ']', $this->_elementMatchers);
+ }
+
+ /**
+ * An array with elements that match the given matchers in the same order.
+ *
+ * @factory contains ...
+ */
+ public static function arrayContaining(/* args... */)
+ {
+ $args = func_get_args();
+
+ return new self(Util::createMatcherArray($args));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingKey.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingKey.php
new file mode 100644
index 0000000..523477e
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingKey.php
@@ -0,0 +1,75 @@
+_keyMatcher = $keyMatcher;
+ }
+
+ protected function matchesSafely($array)
+ {
+ foreach ($array as $key => $element) {
+ if ($this->_keyMatcher->matches($key)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function describeMismatchSafely($array, Description $mismatchDescription)
+ {
+ //Not using appendValueList() so that keys can be shown
+ $mismatchDescription->appendText('array was ')
+ ->appendText('[')
+ ;
+ $loop = false;
+ foreach ($array as $key => $value) {
+ if ($loop) {
+ $mismatchDescription->appendText(', ');
+ }
+ $mismatchDescription->appendValue($key)->appendText(' => ')->appendValue($value);
+ $loop = true;
+ }
+ $mismatchDescription->appendText(']');
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description
+ ->appendText('array with key ')
+ ->appendDescriptionOf($this->_keyMatcher)
+ ;
+ }
+
+ /**
+ * Evaluates to true if any key in an array matches the given matcher.
+ *
+ * @param mixed $key as a {@link Hamcrest\Matcher} or a value.
+ *
+ * @return \Hamcrest\Arrays\IsArrayContainingKey
+ * @factory hasKey
+ */
+ public static function hasKeyInArray($key)
+ {
+ return new self(Util::wrapValueWithIsEqual($key));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingKeyValuePair.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingKeyValuePair.php
new file mode 100644
index 0000000..9ac3eba
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayContainingKeyValuePair.php
@@ -0,0 +1,80 @@
+_keyMatcher = $keyMatcher;
+ $this->_valueMatcher = $valueMatcher;
+ }
+
+ protected function matchesSafely($array)
+ {
+ foreach ($array as $key => $value) {
+ if ($this->_keyMatcher->matches($key) && $this->_valueMatcher->matches($value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function describeMismatchSafely($array, Description $mismatchDescription)
+ {
+ //Not using appendValueList() so that keys can be shown
+ $mismatchDescription->appendText('array was ')
+ ->appendText('[')
+ ;
+ $loop = false;
+ foreach ($array as $key => $value) {
+ if ($loop) {
+ $mismatchDescription->appendText(', ');
+ }
+ $mismatchDescription->appendValue($key)->appendText(' => ')->appendValue($value);
+ $loop = true;
+ }
+ $mismatchDescription->appendText(']');
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('array containing [')
+ ->appendDescriptionOf($this->_keyMatcher)
+ ->appendText(' => ')
+ ->appendDescriptionOf($this->_valueMatcher)
+ ->appendText(']')
+ ;
+ }
+
+ /**
+ * Test if an array has both an key and value in parity with each other.
+ *
+ * @factory hasEntry
+ */
+ public static function hasKeyValuePair($key, $value)
+ {
+ return new self(
+ Util::wrapValueWithIsEqual($key),
+ Util::wrapValueWithIsEqual($value)
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayWithSize.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayWithSize.php
new file mode 100644
index 0000000..074375c
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/IsArrayWithSize.php
@@ -0,0 +1,73 @@
+_elementMatchers = $elementMatchers;
+ $this->_mismatchDescription = $mismatchDescription;
+ }
+
+ public function matches($item)
+ {
+ return $this->_isNotSurplus($item) && $this->_isMatched($item);
+ }
+
+ public function isFinished($items)
+ {
+ if (empty($this->_elementMatchers)) {
+ return true;
+ }
+
+ $this->_mismatchDescription
+ ->appendText('No item matches: ')->appendList('', ', ', '', $this->_elementMatchers)
+ ->appendText(' in ')->appendValueList('[', ', ', ']', $items)
+ ;
+
+ return false;
+ }
+
+ // -- Private Methods
+
+ private function _isNotSurplus($item)
+ {
+ if (empty($this->_elementMatchers)) {
+ $this->_mismatchDescription->appendText('Not matched: ')->appendValue($item);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private function _isMatched($item)
+ {
+ /** @var $matcher \Hamcrest\Matcher */
+ foreach ($this->_elementMatchers as $i => $matcher) {
+ if ($matcher->matches($item)) {
+ unset($this->_elementMatchers[$i]);
+
+ return true;
+ }
+ }
+
+ $this->_mismatchDescription->appendText('Not matched: ')->appendValue($item);
+
+ return false;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/SeriesMatchingOnce.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/SeriesMatchingOnce.php
new file mode 100644
index 0000000..12a912d
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Arrays/SeriesMatchingOnce.php
@@ -0,0 +1,75 @@
+_elementMatchers = $elementMatchers;
+ $this->_keys = array_keys($elementMatchers);
+ $this->_mismatchDescription = $mismatchDescription;
+ }
+
+ public function matches($item)
+ {
+ return $this->_isNotSurplus($item) && $this->_isMatched($item);
+ }
+
+ public function isFinished()
+ {
+ if (!empty($this->_elementMatchers)) {
+ $nextMatcher = current($this->_elementMatchers);
+ $this->_mismatchDescription->appendText('No item matched: ')->appendDescriptionOf($nextMatcher);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ // -- Private Methods
+
+ private function _isNotSurplus($item)
+ {
+ if (empty($this->_elementMatchers)) {
+ $this->_mismatchDescription->appendText('Not matched: ')->appendValue($item);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private function _isMatched($item)
+ {
+ $this->_nextMatchKey = array_shift($this->_keys);
+ $nextMatcher = array_shift($this->_elementMatchers);
+
+ if (!$nextMatcher->matches($item)) {
+ $this->_describeMismatch($nextMatcher, $item);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private function _describeMismatch(Matcher $matcher, $item)
+ {
+ $this->_mismatchDescription->appendText('item with key ' . $this->_nextMatchKey . ': ');
+ $matcher->describeMismatch($item, $this->_mismatchDescription);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/AssertionError.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/AssertionError.php
new file mode 100644
index 0000000..3a2a0e7
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/AssertionError.php
@@ -0,0 +1,10 @@
+append($text);
+
+ return $this;
+ }
+
+ public function appendDescriptionOf(SelfDescribing $value)
+ {
+ $value->describeTo($this);
+
+ return $this;
+ }
+
+ public function appendValue($value)
+ {
+ if (is_null($value)) {
+ $this->append('null');
+ } elseif (is_string($value)) {
+ $this->_toPhpSyntax($value);
+ } elseif (is_float($value)) {
+ $this->append('<');
+ $this->append($value);
+ $this->append('F>');
+ } elseif (is_bool($value)) {
+ $this->append('<');
+ $this->append($value ? 'true' : 'false');
+ $this->append('>');
+ } elseif (is_array($value) || $value instanceof \Iterator || $value instanceof \IteratorAggregate) {
+ $this->appendValueList('[', ', ', ']', $value);
+ } elseif (is_object($value) && !method_exists($value, '__toString')) {
+ $this->append('<');
+ $this->append(get_class($value));
+ $this->append('>');
+ } else {
+ $this->append('<');
+ $this->append($value);
+ $this->append('>');
+ }
+
+ return $this;
+ }
+
+ public function appendValueList($start, $separator, $end, $values)
+ {
+ $list = array();
+ foreach ($values as $v) {
+ $list[] = new SelfDescribingValue($v);
+ }
+
+ $this->appendList($start, $separator, $end, $list);
+
+ return $this;
+ }
+
+ public function appendList($start, $separator, $end, $values)
+ {
+ $this->append($start);
+
+ $separate = false;
+
+ foreach ($values as $value) {
+ /*if (!($value instanceof Hamcrest\SelfDescribing)) {
+ $value = new Hamcrest\Internal\SelfDescribingValue($value);
+ }*/
+
+ if ($separate) {
+ $this->append($separator);
+ }
+
+ $this->appendDescriptionOf($value);
+
+ $separate = true;
+ }
+
+ $this->append($end);
+
+ return $this;
+ }
+
+ // -- Protected Methods
+
+ /**
+ * Append the String $str to the description.
+ */
+ abstract protected function append($str);
+
+ // -- Private Methods
+
+ private function _toPhpSyntax($value)
+ {
+ $str = '"';
+ for ($i = 0, $len = strlen($value); $i < $len; ++$i) {
+ switch ($value[$i]) {
+ case '"':
+ $str .= '\\"';
+ break;
+
+ case "\t":
+ $str .= '\\t';
+ break;
+
+ case "\r":
+ $str .= '\\r';
+ break;
+
+ case "\n":
+ $str .= '\\n';
+ break;
+
+ default:
+ $str .= $value[$i];
+ }
+ }
+ $str .= '"';
+ $this->append($str);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/BaseMatcher.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/BaseMatcher.php
new file mode 100644
index 0000000..0605569
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/BaseMatcher.php
@@ -0,0 +1,30 @@
+appendText('was ')->appendValue($item);
+ }
+
+ public function __toString()
+ {
+ return StringDescription::toString($this);
+ }
+
+ public function __invoke()
+ {
+ return call_user_func_array(array($this, 'matches'), func_get_args());
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Collection/IsEmptyTraversable.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Collection/IsEmptyTraversable.php
new file mode 100644
index 0000000..8ab58ea
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Collection/IsEmptyTraversable.php
@@ -0,0 +1,71 @@
+_empty = $empty;
+ }
+
+ public function matches($item)
+ {
+ if (!$item instanceof \Traversable) {
+ return false;
+ }
+
+ foreach ($item as $value) {
+ return !$this->_empty;
+ }
+
+ return $this->_empty;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText($this->_empty ? 'an empty traversable' : 'a non-empty traversable');
+ }
+
+ /**
+ * Returns true if traversable is empty.
+ *
+ * @factory
+ */
+ public static function emptyTraversable()
+ {
+ if (!self::$_INSTANCE) {
+ self::$_INSTANCE = new self;
+ }
+
+ return self::$_INSTANCE;
+ }
+
+ /**
+ * Returns true if traversable is not empty.
+ *
+ * @factory
+ */
+ public static function nonEmptyTraversable()
+ {
+ if (!self::$_NOT_INSTANCE) {
+ self::$_NOT_INSTANCE = new self(false);
+ }
+
+ return self::$_NOT_INSTANCE;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Collection/IsTraversableWithSize.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Collection/IsTraversableWithSize.php
new file mode 100644
index 0000000..c95edc5
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Collection/IsTraversableWithSize.php
@@ -0,0 +1,47 @@
+false.
+ */
+class AllOf extends DiagnosingMatcher
+{
+
+ private $_matchers;
+
+ public function __construct(array $matchers)
+ {
+ Util::checkAllAreMatchers($matchers);
+
+ $this->_matchers = $matchers;
+ }
+
+ public function matchesWithDiagnosticDescription($item, Description $mismatchDescription)
+ {
+ /** @var $matcher \Hamcrest\Matcher */
+ foreach ($this->_matchers as $matcher) {
+ if (!$matcher->matches($item)) {
+ $mismatchDescription->appendDescriptionOf($matcher)->appendText(' ');
+ $matcher->describeMismatch($item, $mismatchDescription);
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendList('(', ' and ', ')', $this->_matchers);
+ }
+
+ /**
+ * Evaluates to true only if ALL of the passed in matchers evaluate to true.
+ *
+ * @factory ...
+ */
+ public static function allOf(/* args... */)
+ {
+ $args = func_get_args();
+
+ return new self(Util::createMatcherArray($args));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/AnyOf.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/AnyOf.php
new file mode 100644
index 0000000..4504279
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/AnyOf.php
@@ -0,0 +1,58 @@
+true.
+ */
+class AnyOf extends ShortcutCombination
+{
+
+ public function __construct(array $matchers)
+ {
+ parent::__construct($matchers);
+ }
+
+ public function matches($item)
+ {
+ return $this->matchesWithShortcut($item, true);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $this->describeToWithOperator($description, 'or');
+ }
+
+ /**
+ * Evaluates to true if ANY of the passed in matchers evaluate to true.
+ *
+ * @factory ...
+ */
+ public static function anyOf(/* args... */)
+ {
+ $args = func_get_args();
+
+ return new self(Util::createMatcherArray($args));
+ }
+
+ /**
+ * Evaluates to false if ANY of the passed in matchers evaluate to true.
+ *
+ * @factory ...
+ */
+ public static function noneOf(/* args... */)
+ {
+ $args = func_get_args();
+
+ return IsNot::not(
+ new self(Util::createMatcherArray($args))
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/CombinableMatcher.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/CombinableMatcher.php
new file mode 100644
index 0000000..e3b4aa7
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/CombinableMatcher.php
@@ -0,0 +1,78 @@
+_matcher = $matcher;
+ }
+
+ public function matches($item)
+ {
+ return $this->_matcher->matches($item);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendDescriptionOf($this->_matcher);
+ }
+
+ /** Diversion from Hamcrest-Java... Logical "and" not permitted */
+ public function andAlso(Matcher $other)
+ {
+ return new self(new AllOf($this->_templatedListWith($other)));
+ }
+
+ /** Diversion from Hamcrest-Java... Logical "or" not permitted */
+ public function orElse(Matcher $other)
+ {
+ return new self(new AnyOf($this->_templatedListWith($other)));
+ }
+
+ /**
+ * This is useful for fluently combining matchers that must both pass.
+ * For example:
+ *
+ * assertThat($string, both(containsString("a"))->andAlso(containsString("b")));
+ *
+ *
+ * @factory
+ */
+ public static function both(Matcher $matcher)
+ {
+ return new self($matcher);
+ }
+
+ /**
+ * This is useful for fluently combining matchers where either may pass,
+ * for example:
+ *
+ * assertThat($string, either(containsString("a"))->orElse(containsString("b")));
+ *
+ *
+ * @factory
+ */
+ public static function either(Matcher $matcher)
+ {
+ return new self($matcher);
+ }
+
+ // -- Private Methods
+
+ private function _templatedListWith(Matcher $other)
+ {
+ return array($this->_matcher, $other);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/DescribedAs.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/DescribedAs.php
new file mode 100644
index 0000000..5b2583f
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/DescribedAs.php
@@ -0,0 +1,68 @@
+_descriptionTemplate = $descriptionTemplate;
+ $this->_matcher = $matcher;
+ $this->_values = $values;
+ }
+
+ public function matches($item)
+ {
+ return $this->_matcher->matches($item);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $textStart = 0;
+ while (preg_match(self::ARG_PATTERN, $this->_descriptionTemplate, $matches, PREG_OFFSET_CAPTURE, $textStart)) {
+ $text = $matches[0][0];
+ $index = $matches[1][0];
+ $offset = $matches[0][1];
+
+ $description->appendText(substr($this->_descriptionTemplate, $textStart, $offset - $textStart));
+ $description->appendValue($this->_values[$index]);
+
+ $textStart = $offset + strlen($text);
+ }
+
+ if ($textStart < strlen($this->_descriptionTemplate)) {
+ $description->appendText(substr($this->_descriptionTemplate, $textStart));
+ }
+ }
+
+ /**
+ * Wraps an existing matcher and overrides the description when it fails.
+ *
+ * @factory ...
+ */
+ public static function describedAs(/* $description, Hamcrest\Matcher $matcher, $values... */)
+ {
+ $args = func_get_args();
+ $description = array_shift($args);
+ $matcher = array_shift($args);
+ $values = $args;
+
+ return new self($description, $matcher, $values);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/Every.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/Every.php
new file mode 100644
index 0000000..d686f8d
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/Every.php
@@ -0,0 +1,56 @@
+_matcher = $matcher;
+ }
+
+ protected function matchesSafelyWithDiagnosticDescription($items, Description $mismatchDescription)
+ {
+ foreach ($items as $item) {
+ if (!$this->_matcher->matches($item)) {
+ $mismatchDescription->appendText('an item ');
+ $this->_matcher->describeMismatch($item, $mismatchDescription);
+
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('every item is ')->appendDescriptionOf($this->_matcher);
+ }
+
+ /**
+ * @param Matcher $itemMatcher
+ * A matcher to apply to every element in an array.
+ *
+ * @return \Hamcrest\Core\Every
+ * Evaluates to TRUE for a collection in which every item matches $itemMatcher
+ *
+ * @factory
+ */
+ public static function everyItem(Matcher $itemMatcher)
+ {
+ return new self($itemMatcher);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/HasToString.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/HasToString.php
new file mode 100644
index 0000000..45bd910
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/HasToString.php
@@ -0,0 +1,56 @@
+toString();
+ }
+
+ return (string) $actual;
+ }
+
+ /**
+ * Does array size satisfy a given matcher?
+ *
+ * @factory
+ */
+ public static function hasToString($matcher)
+ {
+ return new self(Util::wrapValueWithIsEqual($matcher));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/Is.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/Is.php
new file mode 100644
index 0000000..41266dc
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/Is.php
@@ -0,0 +1,57 @@
+_matcher = $matcher;
+ }
+
+ public function matches($arg)
+ {
+ return $this->_matcher->matches($arg);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('is ')->appendDescriptionOf($this->_matcher);
+ }
+
+ public function describeMismatch($item, Description $mismatchDescription)
+ {
+ $this->_matcher->describeMismatch($item, $mismatchDescription);
+ }
+
+ /**
+ * Decorates another Matcher, retaining the behavior but allowing tests
+ * to be slightly more expressive.
+ *
+ * For example: assertThat($cheese, equalTo($smelly))
+ * vs. assertThat($cheese, is(equalTo($smelly)))
+ *
+ * @factory
+ */
+ public static function is($value)
+ {
+ return new self(Util::wrapValueWithIsEqual($value));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsAnything.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsAnything.php
new file mode 100644
index 0000000..f20e6c0
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsAnything.php
@@ -0,0 +1,45 @@
+true.
+ */
+class IsAnything extends BaseMatcher
+{
+
+ private $_message;
+
+ public function __construct($message = 'ANYTHING')
+ {
+ $this->_message = $message;
+ }
+
+ public function matches($item)
+ {
+ return true;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText($this->_message);
+ }
+
+ /**
+ * This matcher always evaluates to true.
+ *
+ * @param string $description A meaningful string used when describing itself.
+ *
+ * @return \Hamcrest\Core\IsAnything
+ * @factory
+ */
+ public static function anything($description = 'ANYTHING')
+ {
+ return new self($description);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsCollectionContaining.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsCollectionContaining.php
new file mode 100644
index 0000000..5e60426
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsCollectionContaining.php
@@ -0,0 +1,93 @@
+_elementMatcher = $elementMatcher;
+ }
+
+ protected function matchesSafely($items)
+ {
+ foreach ($items as $item) {
+ if ($this->_elementMatcher->matches($item)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected function describeMismatchSafely($items, Description $mismatchDescription)
+ {
+ $mismatchDescription->appendText('was ')->appendValue($items);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description
+ ->appendText('a collection containing ')
+ ->appendDescriptionOf($this->_elementMatcher)
+ ;
+ }
+
+ /**
+ * Test if the value is an array containing this matcher.
+ *
+ * Example:
+ *
+ * assertThat(array('a', 'b'), hasItem(equalTo('b')));
+ * //Convenience defaults to equalTo()
+ * assertThat(array('a', 'b'), hasItem('b'));
+ *
+ *
+ * @factory ...
+ */
+ public static function hasItem()
+ {
+ $args = func_get_args();
+ $firstArg = array_shift($args);
+
+ return new self(Util::wrapValueWithIsEqual($firstArg));
+ }
+
+ /**
+ * Test if the value is an array containing elements that match all of these
+ * matchers.
+ *
+ * Example:
+ *
+ * assertThat(array('a', 'b', 'c'), hasItems(equalTo('a'), equalTo('b')));
+ *
+ *
+ * @factory ...
+ */
+ public static function hasItems(/* args... */)
+ {
+ $args = func_get_args();
+ $matchers = array();
+
+ foreach ($args as $arg) {
+ $matchers[] = self::hasItem($arg);
+ }
+
+ return AllOf::allOf($matchers);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsEqual.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsEqual.php
new file mode 100644
index 0000000..523fba0
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsEqual.php
@@ -0,0 +1,44 @@
+_item = $item;
+ }
+
+ public function matches($arg)
+ {
+ return (($arg == $this->_item) && ($this->_item == $arg));
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendValue($this->_item);
+ }
+
+ /**
+ * Is the value equal to another value, as tested by the use of the "=="
+ * comparison operator?
+ *
+ * @factory
+ */
+ public static function equalTo($item)
+ {
+ return new self($item);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsIdentical.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsIdentical.php
new file mode 100644
index 0000000..28f7b36
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsIdentical.php
@@ -0,0 +1,38 @@
+_value = $value;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendValue($this->_value);
+ }
+
+ /**
+ * Tests of the value is identical to $value as tested by the "===" operator.
+ *
+ * @factory
+ */
+ public static function identicalTo($value)
+ {
+ return new self($value);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsInstanceOf.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsInstanceOf.php
new file mode 100644
index 0000000..7a5c92a
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsInstanceOf.php
@@ -0,0 +1,67 @@
+_theClass = $theClass;
+ }
+
+ protected function matchesWithDiagnosticDescription($item, Description $mismatchDescription)
+ {
+ if (!is_object($item)) {
+ $mismatchDescription->appendText('was ')->appendValue($item);
+
+ return false;
+ }
+
+ if (!($item instanceof $this->_theClass)) {
+ $mismatchDescription->appendText('[' . get_class($item) . '] ')
+ ->appendValue($item);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('an instance of ')
+ ->appendText($this->_theClass)
+ ;
+ }
+
+ /**
+ * Is the value an instance of a particular type?
+ * This version assumes no relationship between the required type and
+ * the signature of the method that sets it up, for example in
+ * assertThat($anObject, anInstanceOf('Thing'));
+ *
+ * @factory any
+ */
+ public static function anInstanceOf($theClass)
+ {
+ return new self($theClass);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsNot.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsNot.php
new file mode 100644
index 0000000..167f0d0
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsNot.php
@@ -0,0 +1,44 @@
+_matcher = $matcher;
+ }
+
+ public function matches($arg)
+ {
+ return !$this->_matcher->matches($arg);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('not ')->appendDescriptionOf($this->_matcher);
+ }
+
+ /**
+ * Matches if value does not match $value.
+ *
+ * @factory
+ */
+ public static function not($value)
+ {
+ return new self(Util::wrapValueWithIsEqual($value));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsNull.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsNull.php
new file mode 100644
index 0000000..91a454c
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsNull.php
@@ -0,0 +1,56 @@
+appendText('null');
+ }
+
+ /**
+ * Matches if value is null.
+ *
+ * @factory
+ */
+ public static function nullValue()
+ {
+ if (!self::$_INSTANCE) {
+ self::$_INSTANCE = new self();
+ }
+
+ return self::$_INSTANCE;
+ }
+
+ /**
+ * Matches if value is not null.
+ *
+ * @factory
+ */
+ public static function notNullValue()
+ {
+ if (!self::$_NOT_INSTANCE) {
+ self::$_NOT_INSTANCE = IsNot::not(self::nullValue());
+ }
+
+ return self::$_NOT_INSTANCE;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsSame.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsSame.php
new file mode 100644
index 0000000..8107870
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsSame.php
@@ -0,0 +1,51 @@
+_object = $object;
+ }
+
+ public function matches($object)
+ {
+ return ($object === $this->_object) && ($this->_object === $object);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('sameInstance(')
+ ->appendValue($this->_object)
+ ->appendText(')')
+ ;
+ }
+
+ /**
+ * Creates a new instance of IsSame.
+ *
+ * @param mixed $object
+ * The predicate evaluates to true only when the argument is
+ * this object.
+ *
+ * @return \Hamcrest\Core\IsSame
+ * @factory
+ */
+ public static function sameInstance($object)
+ {
+ return new self($object);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsTypeOf.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsTypeOf.php
new file mode 100644
index 0000000..d24f0f9
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/IsTypeOf.php
@@ -0,0 +1,71 @@
+_theType = strtolower($theType);
+ }
+
+ public function matches($item)
+ {
+ return strtolower(gettype($item)) == $this->_theType;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText(self::getTypeDescription($this->_theType));
+ }
+
+ public function describeMismatch($item, Description $description)
+ {
+ if ($item === null) {
+ $description->appendText('was null');
+ } else {
+ $description->appendText('was ')
+ ->appendText(self::getTypeDescription(strtolower(gettype($item))))
+ ->appendText(' ')
+ ->appendValue($item)
+ ;
+ }
+ }
+
+ public static function getTypeDescription($type)
+ {
+ if ($type == 'null') {
+ return 'null';
+ }
+
+ return (strpos('aeiou', substr($type, 0, 1)) === false ? 'a ' : 'an ')
+ . $type;
+ }
+
+ /**
+ * Is the value a particular built-in type?
+ *
+ * @factory
+ */
+ public static function typeOf($theType)
+ {
+ return new self($theType);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/Set.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/Set.php
new file mode 100644
index 0000000..cdc45d5
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/Set.php
@@ -0,0 +1,95 @@
+
+ * assertThat(array('a', 'b'), set('b'));
+ * assertThat($foo, set('bar'));
+ * assertThat('Server', notSet('defaultPort'));
+ *
+ *
+ * @todo Replace $property with a matcher and iterate all property names.
+ */
+class Set extends BaseMatcher
+{
+
+ private $_property;
+ private $_not;
+
+ public function __construct($property, $not = false)
+ {
+ $this->_property = $property;
+ $this->_not = $not;
+ }
+
+ public function matches($item)
+ {
+ if ($item === null) {
+ return false;
+ }
+ $property = $this->_property;
+ if (is_array($item)) {
+ $result = isset($item[$property]);
+ } elseif (is_object($item)) {
+ $result = isset($item->$property);
+ } elseif (is_string($item)) {
+ $result = isset($item::$$property);
+ } else {
+ throw new \InvalidArgumentException('Must pass an object, array, or class name');
+ }
+
+ return $this->_not ? !$result : $result;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText($this->_not ? 'unset property ' : 'set property ')->appendText($this->_property);
+ }
+
+ public function describeMismatch($item, Description $description)
+ {
+ $value = '';
+ if (!$this->_not) {
+ $description->appendText('was not set');
+ } else {
+ $property = $this->_property;
+ if (is_array($item)) {
+ $value = $item[$property];
+ } elseif (is_object($item)) {
+ $value = $item->$property;
+ } elseif (is_string($item)) {
+ $value = $item::$$property;
+ }
+ parent::describeMismatch($value, $description);
+ }
+ }
+
+ /**
+ * Matches if value (class, object, or array) has named $property.
+ *
+ * @factory
+ */
+ public static function set($property)
+ {
+ return new self($property);
+ }
+
+ /**
+ * Matches if value (class, object, or array) does not have named $property.
+ *
+ * @factory
+ */
+ public static function notSet($property)
+ {
+ return new self($property, true);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/ShortcutCombination.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/ShortcutCombination.php
new file mode 100644
index 0000000..d93db74
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Core/ShortcutCombination.php
@@ -0,0 +1,43 @@
+
+ */
+ private $_matchers;
+
+ public function __construct(array $matchers)
+ {
+ Util::checkAllAreMatchers($matchers);
+
+ $this->_matchers = $matchers;
+ }
+
+ protected function matchesWithShortcut($item, $shortcut)
+ {
+ /** @var $matcher \Hamcrest\Matcher */
+ foreach ($this->_matchers as $matcher) {
+ if ($matcher->matches($item) == $shortcut) {
+ return $shortcut;
+ }
+ }
+
+ return !$shortcut;
+ }
+
+ public function describeToWithOperator(Description $description, $operator)
+ {
+ $description->appendList('(', ' ' . $operator . ' ', ')', $this->_matchers);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Description.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Description.php
new file mode 100644
index 0000000..9a482db
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Description.php
@@ -0,0 +1,70 @@
+matchesWithDiagnosticDescription($item, new NullDescription());
+ }
+
+ public function describeMismatch($item, Description $mismatchDescription)
+ {
+ $this->matchesWithDiagnosticDescription($item, $mismatchDescription);
+ }
+
+ abstract protected function matchesWithDiagnosticDescription($item, Description $mismatchDescription);
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/FeatureMatcher.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/FeatureMatcher.php
new file mode 100644
index 0000000..59f6cc7
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/FeatureMatcher.php
@@ -0,0 +1,67 @@
+featureValueOf() in a subclass to pull out the feature to be
+ * matched against.
+ */
+abstract class FeatureMatcher extends TypeSafeDiagnosingMatcher
+{
+
+ private $_subMatcher;
+ private $_featureDescription;
+ private $_featureName;
+
+ /**
+ * Constructor.
+ *
+ * @param string $type
+ * @param string $subtype
+ * @param \Hamcrest\Matcher $subMatcher The matcher to apply to the feature
+ * @param string $featureDescription Descriptive text to use in describeTo
+ * @param string $featureName Identifying text for mismatch message
+ */
+ public function __construct($type, $subtype, Matcher $subMatcher, $featureDescription, $featureName)
+ {
+ parent::__construct($type, $subtype);
+
+ $this->_subMatcher = $subMatcher;
+ $this->_featureDescription = $featureDescription;
+ $this->_featureName = $featureName;
+ }
+
+ /**
+ * Implement this to extract the interesting feature.
+ *
+ * @param mixed $actual the target object
+ *
+ * @return mixed the feature to be matched
+ */
+ abstract protected function featureValueOf($actual);
+
+ public function matchesSafelyWithDiagnosticDescription($actual, Description $mismatchDescription)
+ {
+ $featureValue = $this->featureValueOf($actual);
+
+ if (!$this->_subMatcher->matches($featureValue)) {
+ $mismatchDescription->appendText($this->_featureName)
+ ->appendText(' was ')->appendValue($featureValue);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ final public function describeTo(Description $description)
+ {
+ $description->appendText($this->_featureDescription)->appendText(' ')
+ ->appendDescriptionOf($this->_subMatcher)
+ ;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Internal/SelfDescribingValue.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Internal/SelfDescribingValue.php
new file mode 100644
index 0000000..995da71
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Internal/SelfDescribingValue.php
@@ -0,0 +1,27 @@
+_value = $value;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendValue($this->_value);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Matcher.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Matcher.php
new file mode 100644
index 0000000..e5dcf09
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Matcher.php
@@ -0,0 +1,50 @@
+
+ * Matcher implementations should NOT directly implement this interface.
+ * Instead, extend the {@link Hamcrest\BaseMatcher} abstract class,
+ * which will ensure that the Matcher API can grow to support
+ * new features and remain compatible with all Matcher implementations.
+ *
+ * For easy access to common Matcher implementations, use the static factory
+ * methods in {@link Hamcrest\CoreMatchers}.
+ *
+ * @see Hamcrest\CoreMatchers
+ * @see Hamcrest\BaseMatcher
+ */
+interface Matcher extends SelfDescribing
+{
+
+ /**
+ * Evaluates the matcher for argument $item.
+ *
+ * @param mixed $item the object against which the matcher is evaluated.
+ *
+ * @return boolean true
if $item matches,
+ * otherwise false
.
+ *
+ * @see Hamcrest\BaseMatcher
+ */
+ public function matches($item);
+
+ /**
+ * Generate a description of why the matcher has not accepted the item.
+ * The description will be part of a larger description of why a matching
+ * failed, so it should be concise.
+ * This method assumes that matches($item)
is false, but
+ * will not check this.
+ *
+ * @param mixed $item The item that the Matcher has rejected.
+ * @param Description $description
+ * @return
+ */
+ public function describeMismatch($item, Description $description);
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/MatcherAssert.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/MatcherAssert.php
new file mode 100644
index 0000000..d546dbe
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/MatcherAssert.php
@@ -0,0 +1,118 @@
+
+ * // With an identifier
+ * assertThat("apple flavour", $apple->flavour(), equalTo("tasty"));
+ * // Without an identifier
+ * assertThat($apple->flavour(), equalTo("tasty"));
+ * // Evaluating a boolean expression
+ * assertThat("some error", $a > $b);
+ * assertThat($a > $b);
+ *
+ */
+ public static function assertThat(/* $args ... */)
+ {
+ $args = func_get_args();
+ switch (count($args)) {
+ case 1:
+ self::$_count++;
+ if (!$args[0]) {
+ throw new AssertionError();
+ }
+ break;
+
+ case 2:
+ self::$_count++;
+ if ($args[1] instanceof Matcher) {
+ self::doAssert('', $args[0], $args[1]);
+ } elseif (!$args[1]) {
+ throw new AssertionError($args[0]);
+ }
+ break;
+
+ case 3:
+ self::$_count++;
+ self::doAssert(
+ $args[0],
+ $args[1],
+ Util::wrapValueWithIsEqual($args[2])
+ );
+ break;
+
+ default:
+ throw new \InvalidArgumentException('assertThat() requires one to three arguments');
+ }
+ }
+
+ /**
+ * Returns the number of assertions performed.
+ *
+ * @return int
+ */
+ public static function getCount()
+ {
+ return self::$_count;
+ }
+
+ /**
+ * Resets the number of assertions performed to zero.
+ */
+ public static function resetCount()
+ {
+ self::$_count = 0;
+ }
+
+ /**
+ * Performs the actual assertion logic.
+ *
+ * If $matcher
doesn't match $actual
,
+ * throws a {@link Hamcrest\AssertionError} with a description
+ * of the failure along with the optional $identifier
.
+ *
+ * @param string $identifier added to the message upon failure
+ * @param mixed $actual value to compare against $matcher
+ * @param \Hamcrest\Matcher $matcher applied to $actual
+ * @throws AssertionError
+ */
+ private static function doAssert($identifier, $actual, Matcher $matcher)
+ {
+ if (!$matcher->matches($actual)) {
+ $description = new StringDescription();
+ if (!empty($identifier)) {
+ $description->appendText($identifier . PHP_EOL);
+ }
+ $description->appendText('Expected: ')
+ ->appendDescriptionOf($matcher)
+ ->appendText(PHP_EOL . ' but: ');
+
+ $matcher->describeMismatch($actual, $description);
+
+ throw new AssertionError((string) $description);
+ }
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Matchers.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Matchers.php
new file mode 100644
index 0000000..23232e4
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Matchers.php
@@ -0,0 +1,713 @@
+
+ * assertThat($string, both(containsString("a"))->andAlso(containsString("b")));
+ *
+ */
+ public static function both(\Hamcrest\Matcher $matcher)
+ {
+ return \Hamcrest\Core\CombinableMatcher::both($matcher);
+ }
+
+ /**
+ * This is useful for fluently combining matchers where either may pass,
+ * for example:
+ *
+ * assertThat($string, either(containsString("a"))->orElse(containsString("b")));
+ *
+ */
+ public static function either(\Hamcrest\Matcher $matcher)
+ {
+ return \Hamcrest\Core\CombinableMatcher::either($matcher);
+ }
+
+ /**
+ * Wraps an existing matcher and overrides the description when it fails.
+ */
+ public static function describedAs(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Core\DescribedAs', 'describedAs'), $args);
+ }
+
+ /**
+ * @param Matcher $itemMatcher
+ * A matcher to apply to every element in an array.
+ *
+ * @return \Hamcrest\Core\Every
+ * Evaluates to TRUE for a collection in which every item matches $itemMatcher
+ */
+ public static function everyItem(\Hamcrest\Matcher $itemMatcher)
+ {
+ return \Hamcrest\Core\Every::everyItem($itemMatcher);
+ }
+
+ /**
+ * Does array size satisfy a given matcher?
+ */
+ public static function hasToString($matcher)
+ {
+ return \Hamcrest\Core\HasToString::hasToString($matcher);
+ }
+
+ /**
+ * Decorates another Matcher, retaining the behavior but allowing tests
+ * to be slightly more expressive.
+ *
+ * For example: assertThat($cheese, equalTo($smelly))
+ * vs. assertThat($cheese, is(equalTo($smelly)))
+ */
+ public static function is($value)
+ {
+ return \Hamcrest\Core\Is::is($value);
+ }
+
+ /**
+ * This matcher always evaluates to true.
+ *
+ * @param string $description A meaningful string used when describing itself.
+ *
+ * @return \Hamcrest\Core\IsAnything
+ */
+ public static function anything($description = 'ANYTHING')
+ {
+ return \Hamcrest\Core\IsAnything::anything($description);
+ }
+
+ /**
+ * Test if the value is an array containing this matcher.
+ *
+ * Example:
+ *
+ * assertThat(array('a', 'b'), hasItem(equalTo('b')));
+ * //Convenience defaults to equalTo()
+ * assertThat(array('a', 'b'), hasItem('b'));
+ *
+ */
+ public static function hasItem(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Core\IsCollectionContaining', 'hasItem'), $args);
+ }
+
+ /**
+ * Test if the value is an array containing elements that match all of these
+ * matchers.
+ *
+ * Example:
+ *
+ * assertThat(array('a', 'b', 'c'), hasItems(equalTo('a'), equalTo('b')));
+ *
+ */
+ public static function hasItems(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Core\IsCollectionContaining', 'hasItems'), $args);
+ }
+
+ /**
+ * Is the value equal to another value, as tested by the use of the "=="
+ * comparison operator?
+ */
+ public static function equalTo($item)
+ {
+ return \Hamcrest\Core\IsEqual::equalTo($item);
+ }
+
+ /**
+ * Tests of the value is identical to $value as tested by the "===" operator.
+ */
+ public static function identicalTo($value)
+ {
+ return \Hamcrest\Core\IsIdentical::identicalTo($value);
+ }
+
+ /**
+ * Is the value an instance of a particular type?
+ * This version assumes no relationship between the required type and
+ * the signature of the method that sets it up, for example in
+ * assertThat($anObject, anInstanceOf('Thing'));
+ */
+ public static function anInstanceOf($theClass)
+ {
+ return \Hamcrest\Core\IsInstanceOf::anInstanceOf($theClass);
+ }
+
+ /**
+ * Is the value an instance of a particular type?
+ * This version assumes no relationship between the required type and
+ * the signature of the method that sets it up, for example in
+ * assertThat($anObject, anInstanceOf('Thing'));
+ */
+ public static function any($theClass)
+ {
+ return \Hamcrest\Core\IsInstanceOf::anInstanceOf($theClass);
+ }
+
+ /**
+ * Matches if value does not match $value.
+ */
+ public static function not($value)
+ {
+ return \Hamcrest\Core\IsNot::not($value);
+ }
+
+ /**
+ * Matches if value is null.
+ */
+ public static function nullValue()
+ {
+ return \Hamcrest\Core\IsNull::nullValue();
+ }
+
+ /**
+ * Matches if value is not null.
+ */
+ public static function notNullValue()
+ {
+ return \Hamcrest\Core\IsNull::notNullValue();
+ }
+
+ /**
+ * Creates a new instance of IsSame.
+ *
+ * @param mixed $object
+ * The predicate evaluates to true only when the argument is
+ * this object.
+ *
+ * @return \Hamcrest\Core\IsSame
+ */
+ public static function sameInstance($object)
+ {
+ return \Hamcrest\Core\IsSame::sameInstance($object);
+ }
+
+ /**
+ * Is the value a particular built-in type?
+ */
+ public static function typeOf($theType)
+ {
+ return \Hamcrest\Core\IsTypeOf::typeOf($theType);
+ }
+
+ /**
+ * Matches if value (class, object, or array) has named $property.
+ */
+ public static function set($property)
+ {
+ return \Hamcrest\Core\Set::set($property);
+ }
+
+ /**
+ * Matches if value (class, object, or array) does not have named $property.
+ */
+ public static function notSet($property)
+ {
+ return \Hamcrest\Core\Set::notSet($property);
+ }
+
+ /**
+ * Matches if value is a number equal to $value within some range of
+ * acceptable error $delta.
+ */
+ public static function closeTo($value, $delta)
+ {
+ return \Hamcrest\Number\IsCloseTo::closeTo($value, $delta);
+ }
+
+ /**
+ * The value is not > $value, nor < $value.
+ */
+ public static function comparesEqualTo($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::comparesEqualTo($value);
+ }
+
+ /**
+ * The value is > $value.
+ */
+ public static function greaterThan($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::greaterThan($value);
+ }
+
+ /**
+ * The value is >= $value.
+ */
+ public static function greaterThanOrEqualTo($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::greaterThanOrEqualTo($value);
+ }
+
+ /**
+ * The value is >= $value.
+ */
+ public static function atLeast($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::greaterThanOrEqualTo($value);
+ }
+
+ /**
+ * The value is < $value.
+ */
+ public static function lessThan($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::lessThan($value);
+ }
+
+ /**
+ * The value is <= $value.
+ */
+ public static function lessThanOrEqualTo($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::lessThanOrEqualTo($value);
+ }
+
+ /**
+ * The value is <= $value.
+ */
+ public static function atMost($value)
+ {
+ return \Hamcrest\Number\OrderingComparison::lessThanOrEqualTo($value);
+ }
+
+ /**
+ * Matches if value is a zero-length string.
+ */
+ public static function isEmptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isEmptyString();
+ }
+
+ /**
+ * Matches if value is a zero-length string.
+ */
+ public static function emptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isEmptyString();
+ }
+
+ /**
+ * Matches if value is null or a zero-length string.
+ */
+ public static function isEmptyOrNullString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isEmptyOrNullString();
+ }
+
+ /**
+ * Matches if value is null or a zero-length string.
+ */
+ public static function nullOrEmptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isEmptyOrNullString();
+ }
+
+ /**
+ * Matches if value is a non-zero-length string.
+ */
+ public static function isNonEmptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isNonEmptyString();
+ }
+
+ /**
+ * Matches if value is a non-zero-length string.
+ */
+ public static function nonEmptyString()
+ {
+ return \Hamcrest\Text\IsEmptyString::isNonEmptyString();
+ }
+
+ /**
+ * Matches if value is a string equal to $string, regardless of the case.
+ */
+ public static function equalToIgnoringCase($string)
+ {
+ return \Hamcrest\Text\IsEqualIgnoringCase::equalToIgnoringCase($string);
+ }
+
+ /**
+ * Matches if value is a string equal to $string, regardless of whitespace.
+ */
+ public static function equalToIgnoringWhiteSpace($string)
+ {
+ return \Hamcrest\Text\IsEqualIgnoringWhiteSpace::equalToIgnoringWhiteSpace($string);
+ }
+
+ /**
+ * Matches if value is a string that matches regular expression $pattern.
+ */
+ public static function matchesPattern($pattern)
+ {
+ return \Hamcrest\Text\MatchesPattern::matchesPattern($pattern);
+ }
+
+ /**
+ * Matches if value is a string that contains $substring.
+ */
+ public static function containsString($substring)
+ {
+ return \Hamcrest\Text\StringContains::containsString($substring);
+ }
+
+ /**
+ * Matches if value is a string that contains $substring regardless of the case.
+ */
+ public static function containsStringIgnoringCase($substring)
+ {
+ return \Hamcrest\Text\StringContainsIgnoringCase::containsStringIgnoringCase($substring);
+ }
+
+ /**
+ * Matches if value contains $substrings in a constrained order.
+ */
+ public static function stringContainsInOrder(/* args... */)
+ {
+ $args = func_get_args();
+ return call_user_func_array(array('\Hamcrest\Text\StringContainsInOrder', 'stringContainsInOrder'), $args);
+ }
+
+ /**
+ * Matches if value is a string that ends with $substring.
+ */
+ public static function endsWith($substring)
+ {
+ return \Hamcrest\Text\StringEndsWith::endsWith($substring);
+ }
+
+ /**
+ * Matches if value is a string that starts with $substring.
+ */
+ public static function startsWith($substring)
+ {
+ return \Hamcrest\Text\StringStartsWith::startsWith($substring);
+ }
+
+ /**
+ * Is the value an array?
+ */
+ public static function arrayValue()
+ {
+ return \Hamcrest\Type\IsArray::arrayValue();
+ }
+
+ /**
+ * Is the value a boolean?
+ */
+ public static function booleanValue()
+ {
+ return \Hamcrest\Type\IsBoolean::booleanValue();
+ }
+
+ /**
+ * Is the value a boolean?
+ */
+ public static function boolValue()
+ {
+ return \Hamcrest\Type\IsBoolean::booleanValue();
+ }
+
+ /**
+ * Is the value callable?
+ */
+ public static function callableValue()
+ {
+ return \Hamcrest\Type\IsCallable::callableValue();
+ }
+
+ /**
+ * Is the value a float/double?
+ */
+ public static function doubleValue()
+ {
+ return \Hamcrest\Type\IsDouble::doubleValue();
+ }
+
+ /**
+ * Is the value a float/double?
+ */
+ public static function floatValue()
+ {
+ return \Hamcrest\Type\IsDouble::doubleValue();
+ }
+
+ /**
+ * Is the value an integer?
+ */
+ public static function integerValue()
+ {
+ return \Hamcrest\Type\IsInteger::integerValue();
+ }
+
+ /**
+ * Is the value an integer?
+ */
+ public static function intValue()
+ {
+ return \Hamcrest\Type\IsInteger::integerValue();
+ }
+
+ /**
+ * Is the value a numeric?
+ */
+ public static function numericValue()
+ {
+ return \Hamcrest\Type\IsNumeric::numericValue();
+ }
+
+ /**
+ * Is the value an object?
+ */
+ public static function objectValue()
+ {
+ return \Hamcrest\Type\IsObject::objectValue();
+ }
+
+ /**
+ * Is the value an object?
+ */
+ public static function anObject()
+ {
+ return \Hamcrest\Type\IsObject::objectValue();
+ }
+
+ /**
+ * Is the value a resource?
+ */
+ public static function resourceValue()
+ {
+ return \Hamcrest\Type\IsResource::resourceValue();
+ }
+
+ /**
+ * Is the value a scalar (boolean, integer, double, or string)?
+ */
+ public static function scalarValue()
+ {
+ return \Hamcrest\Type\IsScalar::scalarValue();
+ }
+
+ /**
+ * Is the value a string?
+ */
+ public static function stringValue()
+ {
+ return \Hamcrest\Type\IsString::stringValue();
+ }
+
+ /**
+ * Wraps $matcher
with {@link Hamcrest\Core\IsEqual)
+ * if it's not a matcher and the XPath in count()
+ * if it's an integer.
+ */
+ public static function hasXPath($xpath, $matcher = null)
+ {
+ return \Hamcrest\Xml\HasXPath::hasXPath($xpath, $matcher);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/NullDescription.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/NullDescription.php
new file mode 100644
index 0000000..aae8e46
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/NullDescription.php
@@ -0,0 +1,43 @@
+_value = $value;
+ $this->_delta = $delta;
+ }
+
+ protected function matchesSafely($item)
+ {
+ return $this->_actualDelta($item) <= 0.0;
+ }
+
+ protected function describeMismatchSafely($item, Description $mismatchDescription)
+ {
+ $mismatchDescription->appendValue($item)
+ ->appendText(' differed by ')
+ ->appendValue($this->_actualDelta($item))
+ ;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('a numeric value within ')
+ ->appendValue($this->_delta)
+ ->appendText(' of ')
+ ->appendValue($this->_value)
+ ;
+ }
+
+ /**
+ * Matches if value is a number equal to $value within some range of
+ * acceptable error $delta.
+ *
+ * @factory
+ */
+ public static function closeTo($value, $delta)
+ {
+ return new self($value, $delta);
+ }
+
+ // -- Private Methods
+
+ private function _actualDelta($item)
+ {
+ return (abs(($item - $this->_value)) - $this->_delta);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Number/OrderingComparison.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Number/OrderingComparison.php
new file mode 100644
index 0000000..369d0cf
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Number/OrderingComparison.php
@@ -0,0 +1,132 @@
+_value = $value;
+ $this->_minCompare = $minCompare;
+ $this->_maxCompare = $maxCompare;
+ }
+
+ protected function matchesSafely($other)
+ {
+ $compare = $this->_compare($this->_value, $other);
+
+ return ($this->_minCompare <= $compare) && ($compare <= $this->_maxCompare);
+ }
+
+ protected function describeMismatchSafely($item, Description $mismatchDescription)
+ {
+ $mismatchDescription
+ ->appendValue($item)->appendText(' was ')
+ ->appendText($this->_comparison($this->_compare($this->_value, $item)))
+ ->appendText(' ')->appendValue($this->_value)
+ ;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('a value ')
+ ->appendText($this->_comparison($this->_minCompare))
+ ;
+ if ($this->_minCompare != $this->_maxCompare) {
+ $description->appendText(' or ')
+ ->appendText($this->_comparison($this->_maxCompare))
+ ;
+ }
+ $description->appendText(' ')->appendValue($this->_value);
+ }
+
+ /**
+ * The value is not > $value, nor < $value.
+ *
+ * @factory
+ */
+ public static function comparesEqualTo($value)
+ {
+ return new self($value, 0, 0);
+ }
+
+ /**
+ * The value is > $value.
+ *
+ * @factory
+ */
+ public static function greaterThan($value)
+ {
+ return new self($value, -1, -1);
+ }
+
+ /**
+ * The value is >= $value.
+ *
+ * @factory atLeast
+ */
+ public static function greaterThanOrEqualTo($value)
+ {
+ return new self($value, -1, 0);
+ }
+
+ /**
+ * The value is < $value.
+ *
+ * @factory
+ */
+ public static function lessThan($value)
+ {
+ return new self($value, 1, 1);
+ }
+
+ /**
+ * The value is <= $value.
+ *
+ * @factory atMost
+ */
+ public static function lessThanOrEqualTo($value)
+ {
+ return new self($value, 0, 1);
+ }
+
+ // -- Private Methods
+
+ private function _compare($left, $right)
+ {
+ $a = $left;
+ $b = $right;
+
+ if ($a < $b) {
+ return -1;
+ } elseif ($a == $b) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+ private function _comparison($compare)
+ {
+ if ($compare > 0) {
+ return 'less than';
+ } elseif ($compare == 0) {
+ return 'equal to';
+ } else {
+ return 'greater than';
+ }
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/SelfDescribing.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/SelfDescribing.php
new file mode 100644
index 0000000..872fdf9
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/SelfDescribing.php
@@ -0,0 +1,23 @@
+_out = (string) $out;
+ }
+
+ public function __toString()
+ {
+ return $this->_out;
+ }
+
+ /**
+ * Return the description of a {@link Hamcrest\SelfDescribing} object as a
+ * String.
+ *
+ * @param \Hamcrest\SelfDescribing $selfDescribing
+ * The object to be described.
+ *
+ * @return string
+ * The description of the object.
+ */
+ public static function toString(SelfDescribing $selfDescribing)
+ {
+ $self = new self();
+
+ return (string) $self->appendDescriptionOf($selfDescribing);
+ }
+
+ /**
+ * Alias for {@link toString()}.
+ */
+ public static function asString(SelfDescribing $selfDescribing)
+ {
+ return self::toString($selfDescribing);
+ }
+
+ // -- Protected Methods
+
+ protected function append($str)
+ {
+ $this->_out .= $str;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/IsEmptyString.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/IsEmptyString.php
new file mode 100644
index 0000000..2ae61b9
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/IsEmptyString.php
@@ -0,0 +1,85 @@
+_empty = $empty;
+ }
+
+ public function matches($item)
+ {
+ return $this->_empty
+ ? ($item === '')
+ : is_string($item) && $item !== '';
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText($this->_empty ? 'an empty string' : 'a non-empty string');
+ }
+
+ /**
+ * Matches if value is a zero-length string.
+ *
+ * @factory emptyString
+ */
+ public static function isEmptyString()
+ {
+ if (!self::$_INSTANCE) {
+ self::$_INSTANCE = new self(true);
+ }
+
+ return self::$_INSTANCE;
+ }
+
+ /**
+ * Matches if value is null or a zero-length string.
+ *
+ * @factory nullOrEmptyString
+ */
+ public static function isEmptyOrNullString()
+ {
+ if (!self::$_NULL_OR_EMPTY_INSTANCE) {
+ self::$_NULL_OR_EMPTY_INSTANCE = AnyOf::anyOf(
+ IsNull::nullvalue(),
+ self::isEmptyString()
+ );
+ }
+
+ return self::$_NULL_OR_EMPTY_INSTANCE;
+ }
+
+ /**
+ * Matches if value is a non-zero-length string.
+ *
+ * @factory nonEmptyString
+ */
+ public static function isNonEmptyString()
+ {
+ if (!self::$_NOT_INSTANCE) {
+ self::$_NOT_INSTANCE = new self(false);
+ }
+
+ return self::$_NOT_INSTANCE;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/IsEqualIgnoringCase.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/IsEqualIgnoringCase.php
new file mode 100644
index 0000000..3836a8c
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/IsEqualIgnoringCase.php
@@ -0,0 +1,52 @@
+_string = $string;
+ }
+
+ protected function matchesSafely($item)
+ {
+ return strtolower($this->_string) === strtolower($item);
+ }
+
+ protected function describeMismatchSafely($item, Description $mismatchDescription)
+ {
+ $mismatchDescription->appendText('was ')->appendText($item);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('equalToIgnoringCase(')
+ ->appendValue($this->_string)
+ ->appendText(')')
+ ;
+ }
+
+ /**
+ * Matches if value is a string equal to $string, regardless of the case.
+ *
+ * @factory
+ */
+ public static function equalToIgnoringCase($string)
+ {
+ return new self($string);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/IsEqualIgnoringWhiteSpace.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/IsEqualIgnoringWhiteSpace.php
new file mode 100644
index 0000000..853692b
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/IsEqualIgnoringWhiteSpace.php
@@ -0,0 +1,66 @@
+_string = $string;
+ }
+
+ protected function matchesSafely($item)
+ {
+ return (strtolower($this->_stripSpace($item))
+ === strtolower($this->_stripSpace($this->_string)));
+ }
+
+ protected function describeMismatchSafely($item, Description $mismatchDescription)
+ {
+ $mismatchDescription->appendText('was ')->appendText($item);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('equalToIgnoringWhiteSpace(')
+ ->appendValue($this->_string)
+ ->appendText(')')
+ ;
+ }
+
+ /**
+ * Matches if value is a string equal to $string, regardless of whitespace.
+ *
+ * @factory
+ */
+ public static function equalToIgnoringWhiteSpace($string)
+ {
+ return new self($string);
+ }
+
+ // -- Private Methods
+
+ private function _stripSpace($string)
+ {
+ $parts = preg_split("/[\r\n\t ]+/", $string);
+ foreach ($parts as $i => $part) {
+ $parts[$i] = trim($part, " \r\n\t");
+ }
+
+ return trim(implode(' ', $parts), " \r\n\t");
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/MatchesPattern.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/MatchesPattern.php
new file mode 100644
index 0000000..fa0d68e
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/MatchesPattern.php
@@ -0,0 +1,40 @@
+_substring, (string) $item) >= 1;
+ }
+
+ protected function relationship()
+ {
+ return 'matching';
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringContains.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringContains.php
new file mode 100644
index 0000000..b92786b
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringContains.php
@@ -0,0 +1,45 @@
+_substring);
+ }
+
+ /**
+ * Matches if value is a string that contains $substring.
+ *
+ * @factory
+ */
+ public static function containsString($substring)
+ {
+ return new self($substring);
+ }
+
+ // -- Protected Methods
+
+ protected function evalSubstringOf($item)
+ {
+ return (false !== strpos((string) $item, $this->_substring));
+ }
+
+ protected function relationship()
+ {
+ return 'containing';
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringContainsIgnoringCase.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringContainsIgnoringCase.php
new file mode 100644
index 0000000..69f37c2
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringContainsIgnoringCase.php
@@ -0,0 +1,40 @@
+_substring));
+ }
+
+ protected function relationship()
+ {
+ return 'containing in any case';
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringContainsInOrder.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringContainsInOrder.php
new file mode 100644
index 0000000..e75de65
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringContainsInOrder.php
@@ -0,0 +1,66 @@
+_substrings = $substrings;
+ }
+
+ protected function matchesSafely($item)
+ {
+ $fromIndex = 0;
+
+ foreach ($this->_substrings as $substring) {
+ if (false === $fromIndex = strpos($item, $substring, $fromIndex)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected function describeMismatchSafely($item, Description $mismatchDescription)
+ {
+ $mismatchDescription->appendText('was ')->appendText($item);
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('a string containing ')
+ ->appendValueList('', ', ', '', $this->_substrings)
+ ->appendText(' in order')
+ ;
+ }
+
+ /**
+ * Matches if value contains $substrings in a constrained order.
+ *
+ * @factory ...
+ */
+ public static function stringContainsInOrder(/* args... */)
+ {
+ $args = func_get_args();
+
+ if (isset($args[0]) && is_array($args[0])) {
+ $args = $args[0];
+ }
+
+ return new self($args);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringEndsWith.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringEndsWith.php
new file mode 100644
index 0000000..f802ee4
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringEndsWith.php
@@ -0,0 +1,40 @@
+_substring))) === $this->_substring);
+ }
+
+ protected function relationship()
+ {
+ return 'ending with';
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringStartsWith.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringStartsWith.php
new file mode 100644
index 0000000..79c9565
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/StringStartsWith.php
@@ -0,0 +1,40 @@
+_substring)) === $this->_substring);
+ }
+
+ protected function relationship()
+ {
+ return 'starting with';
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/SubstringMatcher.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/SubstringMatcher.php
new file mode 100644
index 0000000..e560ad6
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Text/SubstringMatcher.php
@@ -0,0 +1,45 @@
+_substring = $substring;
+ }
+
+ protected function matchesSafely($item)
+ {
+ return $this->evalSubstringOf($item);
+ }
+
+ protected function describeMismatchSafely($item, Description $mismatchDescription)
+ {
+ $mismatchDescription->appendText('was "')->appendText($item)->appendText('"');
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('a string ')
+ ->appendText($this->relationship())
+ ->appendText(' ')
+ ->appendValue($this->_substring)
+ ;
+ }
+
+ abstract protected function evalSubstringOf($string);
+
+ abstract protected function relationship();
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Type/IsArray.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Type/IsArray.php
new file mode 100644
index 0000000..9179102
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Type/IsArray.php
@@ -0,0 +1,32 @@
+isHexadecimal($item)) {
+ return true;
+ }
+
+ return is_numeric($item);
+ }
+
+ /**
+ * Return if the string passed is a valid hexadecimal number.
+ * This check is necessary because PHP 7 doesn't recognize hexadecimal string as numeric anymore.
+ *
+ * @param mixed $item
+ * @return boolean
+ */
+ private function isHexadecimal($item)
+ {
+ if (is_string($item) && preg_match('/^0x(.*)$/', $item, $matches)) {
+ return ctype_xdigit($matches[1]);
+ }
+
+ return false;
+ }
+
+ /**
+ * Is the value a numeric?
+ *
+ * @factory
+ */
+ public static function numericValue()
+ {
+ return new self;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Type/IsObject.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Type/IsObject.php
new file mode 100644
index 0000000..65918fc
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Type/IsObject.php
@@ -0,0 +1,32 @@
+matchesSafelyWithDiagnosticDescription($item, new NullDescription());
+ }
+
+ final public function describeMismatchSafely($item, Description $mismatchDescription)
+ {
+ $this->matchesSafelyWithDiagnosticDescription($item, $mismatchDescription);
+ }
+
+ // -- Protected Methods
+
+ /**
+ * Subclasses should implement these. The item will already have been checked for
+ * the specific type.
+ */
+ abstract protected function matchesSafelyWithDiagnosticDescription($item, Description $mismatchDescription);
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/TypeSafeMatcher.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/TypeSafeMatcher.php
new file mode 100644
index 0000000..56e299a
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/TypeSafeMatcher.php
@@ -0,0 +1,107 @@
+_expectedType = $expectedType;
+ $this->_expectedSubtype = $expectedSubtype;
+ }
+
+ final public function matches($item)
+ {
+ return $this->_isSafeType($item) && $this->matchesSafely($item);
+ }
+
+ final public function describeMismatch($item, Description $mismatchDescription)
+ {
+ if (!$this->_isSafeType($item)) {
+ parent::describeMismatch($item, $mismatchDescription);
+ } else {
+ $this->describeMismatchSafely($item, $mismatchDescription);
+ }
+ }
+
+ // -- Protected Methods
+
+ /**
+ * The item will already have been checked for the specific type and subtype.
+ */
+ abstract protected function matchesSafely($item);
+
+ /**
+ * The item will already have been checked for the specific type and subtype.
+ */
+ abstract protected function describeMismatchSafely($item, Description $mismatchDescription);
+
+ // -- Private Methods
+
+ private function _isSafeType($value)
+ {
+ switch ($this->_expectedType) {
+
+ case self::TYPE_ANY:
+ return true;
+
+ case self::TYPE_STRING:
+ return is_string($value) || is_numeric($value);
+
+ case self::TYPE_NUMERIC:
+ return is_numeric($value) || is_string($value);
+
+ case self::TYPE_ARRAY:
+ return is_array($value);
+
+ case self::TYPE_OBJECT:
+ return is_object($value)
+ && ($this->_expectedSubtype === null
+ || $value instanceof $this->_expectedSubtype);
+
+ case self::TYPE_RESOURCE:
+ return is_resource($value)
+ && ($this->_expectedSubtype === null
+ || get_resource_type($value) == $this->_expectedSubtype);
+
+ case self::TYPE_BOOLEAN:
+ return true;
+
+ default:
+ return true;
+
+ }
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Util.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Util.php
new file mode 100644
index 0000000..169b036
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Util.php
@@ -0,0 +1,76 @@
+ all items are
+ */
+ public static function createMatcherArray(array $items)
+ {
+ //Extract single array item
+ if (count($items) == 1 && is_array($items[0])) {
+ $items = $items[0];
+ }
+
+ //Replace non-matchers
+ foreach ($items as &$item) {
+ if (!($item instanceof Matcher)) {
+ $item = Core\IsEqual::equalTo($item);
+ }
+ }
+
+ return $items;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Xml/HasXPath.php b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Xml/HasXPath.php
new file mode 100644
index 0000000..d9764e4
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest/Xml/HasXPath.php
@@ -0,0 +1,195 @@
+_xpath = $xpath;
+ $this->_matcher = $matcher;
+ }
+
+ /**
+ * Matches if the XPath matches against the DOM node and the matcher.
+ *
+ * @param string|\DOMNode $actual
+ * @param Description $mismatchDescription
+ * @return bool
+ */
+ protected function matchesWithDiagnosticDescription($actual, Description $mismatchDescription)
+ {
+ if (is_string($actual)) {
+ $actual = $this->createDocument($actual);
+ } elseif (!$actual instanceof \DOMNode) {
+ $mismatchDescription->appendText('was ')->appendValue($actual);
+
+ return false;
+ }
+ $result = $this->evaluate($actual);
+ if ($result instanceof \DOMNodeList) {
+ return $this->matchesContent($result, $mismatchDescription);
+ } else {
+ return $this->matchesExpression($result, $mismatchDescription);
+ }
+ }
+
+ /**
+ * Creates and returns a DOMDocument
from the given
+ * XML or HTML string.
+ *
+ * @param string $text
+ * @return \DOMDocument built from $text
+ * @throws \InvalidArgumentException if the document is not valid
+ */
+ protected function createDocument($text)
+ {
+ $document = new \DOMDocument();
+ if (preg_match('/^\s*<\?xml/', $text)) {
+ if (!@$document->loadXML($text)) {
+ throw new \InvalidArgumentException('Must pass a valid XML document');
+ }
+ } else {
+ if (!@$document->loadHTML($text)) {
+ throw new \InvalidArgumentException('Must pass a valid HTML or XHTML document');
+ }
+ }
+
+ return $document;
+ }
+
+ /**
+ * Applies the configured XPath to the DOM node and returns either
+ * the result if it's an expression or the node list if it's a query.
+ *
+ * @param \DOMNode $node context from which to issue query
+ * @return mixed result of expression or DOMNodeList from query
+ */
+ protected function evaluate(\DOMNode $node)
+ {
+ if ($node instanceof \DOMDocument) {
+ $xpathDocument = new \DOMXPath($node);
+
+ return $xpathDocument->evaluate($this->_xpath);
+ } else {
+ $xpathDocument = new \DOMXPath($node->ownerDocument);
+
+ return $xpathDocument->evaluate($this->_xpath, $node);
+ }
+ }
+
+ /**
+ * Matches if the list of nodes is not empty and the content of at least
+ * one node matches the configured matcher, if supplied.
+ *
+ * @param \DOMNodeList $nodes selected by the XPath query
+ * @param Description $mismatchDescription
+ * @return bool
+ */
+ protected function matchesContent(\DOMNodeList $nodes, Description $mismatchDescription)
+ {
+ if ($nodes->length == 0) {
+ $mismatchDescription->appendText('XPath returned no results');
+ } elseif ($this->_matcher === null) {
+ return true;
+ } else {
+ foreach ($nodes as $node) {
+ if ($this->_matcher->matches($node->textContent)) {
+ return true;
+ }
+ }
+ $content = array();
+ foreach ($nodes as $node) {
+ $content[] = $node->textContent;
+ }
+ $mismatchDescription->appendText('XPath returned ')
+ ->appendValue($content);
+ }
+
+ return false;
+ }
+
+ /**
+ * Matches if the result of the XPath expression matches the configured
+ * matcher or evaluates to true
if there is none.
+ *
+ * @param mixed $result result of the XPath expression
+ * @param Description $mismatchDescription
+ * @return bool
+ */
+ protected function matchesExpression($result, Description $mismatchDescription)
+ {
+ if ($this->_matcher === null) {
+ if ($result) {
+ return true;
+ }
+ $mismatchDescription->appendText('XPath expression result was ')
+ ->appendValue($result);
+ } else {
+ if ($this->_matcher->matches($result)) {
+ return true;
+ }
+ $mismatchDescription->appendText('XPath expression result ');
+ $this->_matcher->describeMismatch($result, $mismatchDescription);
+ }
+
+ return false;
+ }
+
+ public function describeTo(Description $description)
+ {
+ $description->appendText('XML or HTML document with XPath "')
+ ->appendText($this->_xpath)
+ ->appendText('"');
+ if ($this->_matcher !== null) {
+ $description->appendText(' ');
+ $this->_matcher->describeTo($description);
+ }
+ }
+
+ /**
+ * Wraps $matcher
with {@link Hamcrest\Core\IsEqual)
+ * if it's not a matcher and the XPath in count()
+ * if it's an integer.
+ *
+ * @factory
+ */
+ public static function hasXPath($xpath, $matcher = null)
+ {
+ if ($matcher === null || $matcher instanceof Matcher) {
+ return new self($xpath, $matcher);
+ } elseif (is_int($matcher) && strpos($xpath, 'count(') !== 0) {
+ $xpath = 'count(' . $xpath . ')';
+ }
+
+ return new self($xpath, IsEqual::equalTo($matcher));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/AbstractMatcherTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/AbstractMatcherTest.php
new file mode 100644
index 0000000..8a1fb2a
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/AbstractMatcherTest.php
@@ -0,0 +1,68 @@
+assertTrue($matcher->matches($arg), $message);
+ }
+
+ public function assertDoesNotMatch(\Hamcrest\Matcher $matcher, $arg, $message)
+ {
+ $this->assertFalse($matcher->matches($arg), $message);
+ }
+
+ public function assertDescription($expected, \Hamcrest\Matcher $matcher)
+ {
+ $description = new \Hamcrest\StringDescription();
+ $description->appendDescriptionOf($matcher);
+ $this->assertEquals($expected, (string) $description, 'Expected description');
+ }
+
+ public function assertMismatchDescription($expected, \Hamcrest\Matcher $matcher, $arg)
+ {
+ $description = new \Hamcrest\StringDescription();
+ $this->assertFalse(
+ $matcher->matches($arg),
+ 'Precondtion: Matcher should not match item'
+ );
+ $matcher->describeMismatch($arg, $description);
+ $this->assertEquals(
+ $expected,
+ (string) $description,
+ 'Expected mismatch description'
+ );
+ }
+
+ public function testIsNullSafe()
+ {
+ //Should not generate any notices
+ $this->createMatcher()->matches(null);
+ $this->createMatcher()->describeMismatch(
+ null,
+ new \Hamcrest\NullDescription()
+ );
+ }
+
+ public function testCopesWithUnknownTypes()
+ {
+ //Should not generate any notices
+ $this->createMatcher()->matches(new UnknownType());
+ $this->createMatcher()->describeMismatch(
+ new UnknownType(),
+ new NullDescription()
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingInAnyOrderTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingInAnyOrderTest.php
new file mode 100644
index 0000000..45d9f13
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingInAnyOrderTest.php
@@ -0,0 +1,54 @@
+assertDescription('[<1>, <2>] in any order', containsInAnyOrder(array(1, 2)));
+ }
+
+ public function testMatchesItemsInAnyOrder()
+ {
+ $this->assertMatches(containsInAnyOrder(array(1, 2, 3)), array(1, 2, 3), 'in order');
+ $this->assertMatches(containsInAnyOrder(array(1, 2, 3)), array(3, 2, 1), 'out of order');
+ $this->assertMatches(containsInAnyOrder(array(1)), array(1), 'single');
+ }
+
+ public function testAppliesMatchersInAnyOrder()
+ {
+ $this->assertMatches(
+ containsInAnyOrder(array(1, 2, 3)),
+ array(1, 2, 3),
+ 'in order'
+ );
+ $this->assertMatches(
+ containsInAnyOrder(array(1, 2, 3)),
+ array(3, 2, 1),
+ 'out of order'
+ );
+ $this->assertMatches(
+ containsInAnyOrder(array(1)),
+ array(1),
+ 'single'
+ );
+ }
+
+ public function testMismatchesItemsInAnyOrder()
+ {
+ $matcher = containsInAnyOrder(array(1, 2, 3));
+
+ $this->assertMismatchDescription('was null', $matcher, null);
+ $this->assertMismatchDescription('No item matches: <1>, <2>, <3> in []', $matcher, array());
+ $this->assertMismatchDescription('No item matches: <2>, <3> in [<1>]', $matcher, array(1));
+ $this->assertMismatchDescription('Not matched: <4>', $matcher, array(4, 3, 2, 1));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingInOrderTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingInOrderTest.php
new file mode 100644
index 0000000..a9e4e5b
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingInOrderTest.php
@@ -0,0 +1,48 @@
+assertDescription('[<1>, <2>]', arrayContaining(array(1, 2)));
+ }
+
+ public function testMatchesItemsInOrder()
+ {
+ $this->assertMatches(arrayContaining(array(1, 2, 3)), array(1, 2, 3), 'in order');
+ $this->assertMatches(arrayContaining(array(1)), array(1), 'single');
+ }
+
+ public function testAppliesMatchersInOrder()
+ {
+ $this->assertMatches(
+ arrayContaining(array(1, 2, 3)),
+ array(1, 2, 3),
+ 'in order'
+ );
+ $this->assertMatches(arrayContaining(array(1)), array(1), 'single');
+ }
+
+ public function testMismatchesItemsInAnyOrder()
+ {
+ if (defined('HHVM_VERSION')) {
+ $this->markTestSkipped('Broken on HHVM.');
+ }
+
+ $matcher = arrayContaining(array(1, 2, 3));
+ $this->assertMismatchDescription('was null', $matcher, null);
+ $this->assertMismatchDescription('No item matched: <1>', $matcher, array());
+ $this->assertMismatchDescription('No item matched: <2>', $matcher, array(1));
+ $this->assertMismatchDescription('item with key 0: was <4>', $matcher, array(4, 3, 2, 1));
+ $this->assertMismatchDescription('item with key 2: was <4>', $matcher, array(1, 2, 4));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingKeyTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingKeyTest.php
new file mode 100644
index 0000000..31770d8
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingKeyTest.php
@@ -0,0 +1,62 @@
+1);
+
+ $this->assertMatches(hasKey('a'), $array, 'Matches single key');
+ }
+
+ public function testMatchesArrayContainingKey()
+ {
+ $array = array('a'=>1, 'b'=>2, 'c'=>3);
+
+ $this->assertMatches(hasKey('a'), $array, 'Matches a');
+ $this->assertMatches(hasKey('c'), $array, 'Matches c');
+ }
+
+ public function testMatchesArrayContainingKeyWithIntegerKeys()
+ {
+ $array = array(1=>'A', 2=>'B');
+
+ assertThat($array, hasKey(1));
+ }
+
+ public function testMatchesArrayContainingKeyWithNumberKeys()
+ {
+ $array = array(1=>'A', 2=>'B');
+
+ assertThat($array, hasKey(1));
+
+ // very ugly version!
+ assertThat($array, IsArrayContainingKey::hasKeyInArray(2));
+ }
+
+ public function testHasReadableDescription()
+ {
+ $this->assertDescription('array with key "a"', hasKey('a'));
+ }
+
+ public function testDoesNotMatchEmptyArray()
+ {
+ $this->assertMismatchDescription('array was []', hasKey('Foo'), array());
+ }
+
+ public function testDoesNotMatchArrayMissingKey()
+ {
+ $array = array('a'=>1, 'b'=>2, 'c'=>3);
+
+ $this->assertMismatchDescription('array was ["a" => <1>, "b" => <2>, "c" => <3>]', hasKey('d'), $array);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingKeyValuePairTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingKeyValuePairTest.php
new file mode 100644
index 0000000..a415f9f
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingKeyValuePairTest.php
@@ -0,0 +1,36 @@
+1, 'b'=>2);
+
+ $this->assertMatches(hasKeyValuePair(equalTo('a'), equalTo(1)), $array, 'matcherA');
+ $this->assertMatches(hasKeyValuePair(equalTo('b'), equalTo(2)), $array, 'matcherB');
+ $this->assertMismatchDescription(
+ 'array was ["a" => <1>, "b" => <2>]',
+ hasKeyValuePair(equalTo('c'), equalTo(3)),
+ $array
+ );
+ }
+
+ public function testDoesNotMatchNull()
+ {
+ $this->assertMismatchDescription('was null', hasKeyValuePair(anything(), anything()), null);
+ }
+
+ public function testHasReadableDescription()
+ {
+ $this->assertDescription('array containing ["a" => <2>]', hasKeyValuePair(equalTo('a'), equalTo(2)));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingTest.php
new file mode 100644
index 0000000..8d5bd81
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayContainingTest.php
@@ -0,0 +1,50 @@
+assertMatches(
+ hasItemInArray('a'),
+ array('a', 'b', 'c'),
+ "should matches array that contains 'a'"
+ );
+ }
+
+ public function testDoesNotMatchAnArrayThatDoesntContainAnElementMatchingTheGivenMatcher()
+ {
+ $this->assertDoesNotMatch(
+ hasItemInArray('a'),
+ array('b', 'c'),
+ "should not matches array that doesn't contain 'a'"
+ );
+ $this->assertDoesNotMatch(
+ hasItemInArray('a'),
+ array(),
+ 'should not match empty array'
+ );
+ }
+
+ public function testDoesNotMatchNull()
+ {
+ $this->assertDoesNotMatch(
+ hasItemInArray('a'),
+ null,
+ 'should not match null'
+ );
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription('an array containing "a"', hasItemInArray('a'));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayTest.php
new file mode 100644
index 0000000..e4db53e
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayTest.php
@@ -0,0 +1,89 @@
+assertMatches(
+ anArray(array(equalTo('a'), equalTo('b'), equalTo('c'))),
+ array('a', 'b', 'c'),
+ 'should match array with matching elements'
+ );
+ }
+
+ public function testDoesNotMatchAnArrayWhenElementsDoNotMatch()
+ {
+ $this->assertDoesNotMatch(
+ anArray(array(equalTo('a'), equalTo('b'))),
+ array('b', 'c'),
+ 'should not match array with different elements'
+ );
+ }
+
+ public function testDoesNotMatchAnArrayOfDifferentSize()
+ {
+ $this->assertDoesNotMatch(
+ anArray(array(equalTo('a'), equalTo('b'))),
+ array('a', 'b', 'c'),
+ 'should not match larger array'
+ );
+ $this->assertDoesNotMatch(
+ anArray(array(equalTo('a'), equalTo('b'))),
+ array('a'),
+ 'should not match smaller array'
+ );
+ }
+
+ public function testDoesNotMatchNull()
+ {
+ $this->assertDoesNotMatch(
+ anArray(array(equalTo('a'))),
+ null,
+ 'should not match null'
+ );
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription(
+ '["a", "b"]',
+ anArray(array(equalTo('a'), equalTo('b')))
+ );
+ }
+
+ public function testHasAReadableMismatchDescriptionWhenKeysDontMatch()
+ {
+ $this->assertMismatchDescription(
+ 'array keys were [<1>, <2>]',
+ anArray(array(equalTo('a'), equalTo('b'))),
+ array(1 => 'a', 2 => 'b')
+ );
+ }
+
+ public function testSupportsMatchesAssociativeArrays()
+ {
+ $this->assertMatches(
+ anArray(array('x'=>equalTo('a'), 'y'=>equalTo('b'), 'z'=>equalTo('c'))),
+ array('x'=>'a', 'y'=>'b', 'z'=>'c'),
+ 'should match associative array with matching elements'
+ );
+ }
+
+ public function testDoesNotMatchAnAssociativeArrayWhenKeysDoNotMatch()
+ {
+ $this->assertDoesNotMatch(
+ anArray(array('x'=>equalTo('a'), 'y'=>equalTo('b'))),
+ array('x'=>'b', 'z'=>'c'),
+ 'should not match array with different keys'
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayWithSizeTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayWithSizeTest.php
new file mode 100644
index 0000000..8413c89
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Array/IsArrayWithSizeTest.php
@@ -0,0 +1,37 @@
+assertMatches(arrayWithSize(equalTo(3)), array(1, 2, 3), 'correct size');
+ $this->assertDoesNotMatch(arrayWithSize(equalTo(2)), array(1, 2, 3), 'incorrect size');
+ }
+
+ public function testProvidesConvenientShortcutForArrayWithSizeEqualTo()
+ {
+ $this->assertMatches(arrayWithSize(3), array(1, 2, 3), 'correct size');
+ $this->assertDoesNotMatch(arrayWithSize(2), array(1, 2, 3), 'incorrect size');
+ }
+
+ public function testEmptyArray()
+ {
+ $this->assertMatches(emptyArray(), array(), 'correct size');
+ $this->assertDoesNotMatch(emptyArray(), array(1), 'incorrect size');
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription('an array with size <3>', arrayWithSize(equalTo(3)));
+ $this->assertDescription('an empty array', emptyArray());
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/BaseMatcherTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/BaseMatcherTest.php
new file mode 100644
index 0000000..833e2c3
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/BaseMatcherTest.php
@@ -0,0 +1,23 @@
+appendText('SOME DESCRIPTION');
+ }
+
+ public function testDescribesItselfWithToStringMethod()
+ {
+ $someMatcher = new \Hamcrest\SomeMatcher();
+ $this->assertEquals('SOME DESCRIPTION', (string) $someMatcher);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Collection/IsEmptyTraversableTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Collection/IsEmptyTraversableTest.php
new file mode 100644
index 0000000..2f15fb4
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Collection/IsEmptyTraversableTest.php
@@ -0,0 +1,77 @@
+assertMatches(
+ emptyTraversable(),
+ new \ArrayObject(array()),
+ 'an empty traversable'
+ );
+ }
+
+ public function testEmptyMatcherDoesNotMatchWhenNotEmpty()
+ {
+ $this->assertDoesNotMatch(
+ emptyTraversable(),
+ new \ArrayObject(array(1, 2, 3)),
+ 'a non-empty traversable'
+ );
+ }
+
+ public function testEmptyMatcherDoesNotMatchNull()
+ {
+ $this->assertDoesNotMatch(
+ emptyTraversable(),
+ null,
+ 'should not match null'
+ );
+ }
+
+ public function testEmptyMatcherHasAReadableDescription()
+ {
+ $this->assertDescription('an empty traversable', emptyTraversable());
+ }
+
+ public function testNonEmptyDoesNotMatchNull()
+ {
+ $this->assertDoesNotMatch(
+ nonEmptyTraversable(),
+ null,
+ 'should not match null'
+ );
+ }
+
+ public function testNonEmptyDoesNotMatchWhenEmpty()
+ {
+ $this->assertDoesNotMatch(
+ nonEmptyTraversable(),
+ new \ArrayObject(array()),
+ 'an empty traversable'
+ );
+ }
+
+ public function testNonEmptyMatchesWhenNotEmpty()
+ {
+ $this->assertMatches(
+ nonEmptyTraversable(),
+ new \ArrayObject(array(1, 2, 3)),
+ 'a non-empty traversable'
+ );
+ }
+
+ public function testNonEmptyNonEmptyMatcherHasAReadableDescription()
+ {
+ $this->assertDescription('a non-empty traversable', nonEmptyTraversable());
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Collection/IsTraversableWithSizeTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Collection/IsTraversableWithSizeTest.php
new file mode 100644
index 0000000..c1c67a7
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Collection/IsTraversableWithSizeTest.php
@@ -0,0 +1,57 @@
+assertMatches(
+ traversableWithSize(equalTo(3)),
+ new \ArrayObject(array(1, 2, 3)),
+ 'correct size'
+ );
+ }
+
+ public function testDoesNotMatchWhenSizeIsIncorrect()
+ {
+ $this->assertDoesNotMatch(
+ traversableWithSize(equalTo(2)),
+ new \ArrayObject(array(1, 2, 3)),
+ 'incorrect size'
+ );
+ }
+
+ public function testDoesNotMatchNull()
+ {
+ $this->assertDoesNotMatch(
+ traversableWithSize(3),
+ null,
+ 'should not match null'
+ );
+ }
+
+ public function testProvidesConvenientShortcutForTraversableWithSizeEqualTo()
+ {
+ $this->assertMatches(
+ traversableWithSize(3),
+ new \ArrayObject(array(1, 2, 3)),
+ 'correct size'
+ );
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription(
+ 'a traversable with size <3>',
+ traversableWithSize(equalTo(3))
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/AllOfTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/AllOfTest.php
new file mode 100644
index 0000000..86b8c27
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/AllOfTest.php
@@ -0,0 +1,56 @@
+assertDescription(
+ '("good" and "bad" and "ugly")',
+ allOf('good', 'bad', 'ugly')
+ );
+ }
+
+ public function testMismatchDescriptionDescribesFirstFailingMatch()
+ {
+ $this->assertMismatchDescription(
+ '"good" was "bad"',
+ allOf('bad', 'good'),
+ 'bad'
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/AnyOfTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/AnyOfTest.php
new file mode 100644
index 0000000..3d62b93
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/AnyOfTest.php
@@ -0,0 +1,79 @@
+assertDescription(
+ '("good" or "bad" or "ugly")',
+ anyOf('good', 'bad', 'ugly')
+ );
+ }
+
+ public function testNoneOfEvaluatesToTheLogicalDisjunctionOfTwoOtherMatchers()
+ {
+ assertThat('good', not(noneOf('bad', 'good')));
+ assertThat('good', not(noneOf('good', 'good')));
+ assertThat('good', not(noneOf('good', 'bad')));
+
+ assertThat('good', noneOf('bad', startsWith('b')));
+ }
+
+ public function testNoneOfEvaluatesToTheLogicalDisjunctionOfManyOtherMatchers()
+ {
+ assertThat('good', not(noneOf('bad', 'good', 'bad', 'bad', 'bad')));
+ assertThat('good', noneOf('bad', 'bad', 'bad', 'bad', 'bad'));
+ }
+
+ public function testNoneOfSupportsMixedTypes()
+ {
+ $combined = noneOf(
+ equalTo(new \Hamcrest\Core\SampleBaseClass('good')),
+ equalTo(new \Hamcrest\Core\SampleBaseClass('ugly')),
+ equalTo(new \Hamcrest\Core\SampleSubClass('good'))
+ );
+
+ assertThat(new \Hamcrest\Core\SampleSubClass('bad'), $combined);
+ }
+
+ public function testNoneOfHasAReadableDescription()
+ {
+ $this->assertDescription(
+ 'not ("good" or "bad" or "ugly")',
+ noneOf('good', 'bad', 'ugly')
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/CombinableMatcherTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/CombinableMatcherTest.php
new file mode 100644
index 0000000..463c754
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/CombinableMatcherTest.php
@@ -0,0 +1,59 @@
+_either_3_or_4 = \Hamcrest\Core\CombinableMatcher::either(equalTo(3))->orElse(equalTo(4));
+ $this->_not_3_and_not_4 = \Hamcrest\Core\CombinableMatcher::both(not(equalTo(3)))->andAlso(not(equalTo(4)));
+ }
+
+ protected function createMatcher()
+ {
+ return \Hamcrest\Core\CombinableMatcher::either(equalTo('irrelevant'))->orElse(equalTo('ignored'));
+ }
+
+ public function testBothAcceptsAndRejects()
+ {
+ assertThat(2, $this->_not_3_and_not_4);
+ assertThat(3, not($this->_not_3_and_not_4));
+ }
+
+ public function testAcceptsAndRejectsThreeAnds()
+ {
+ $tripleAnd = $this->_not_3_and_not_4->andAlso(equalTo(2));
+ assertThat(2, $tripleAnd);
+ assertThat(3, not($tripleAnd));
+ }
+
+ public function testBothDescribesItself()
+ {
+ $this->assertEquals('(not <3> and not <4>)', (string) $this->_not_3_and_not_4);
+ $this->assertMismatchDescription('was <3>', $this->_not_3_and_not_4, 3);
+ }
+
+ public function testEitherAcceptsAndRejects()
+ {
+ assertThat(3, $this->_either_3_or_4);
+ assertThat(6, not($this->_either_3_or_4));
+ }
+
+ public function testAcceptsAndRejectsThreeOrs()
+ {
+ $orTriple = $this->_either_3_or_4->orElse(greaterThan(10));
+
+ assertThat(11, $orTriple);
+ assertThat(9, not($orTriple));
+ }
+
+ public function testEitherDescribesItself()
+ {
+ $this->assertEquals('(<3> or <4>)', (string) $this->_either_3_or_4);
+ $this->assertMismatchDescription('was <6>', $this->_either_3_or_4, 6);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/DescribedAsTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/DescribedAsTest.php
new file mode 100644
index 0000000..673ab41
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/DescribedAsTest.php
@@ -0,0 +1,36 @@
+assertDescription('m1 description', $m1);
+ $this->assertDescription('m2 description', $m2);
+ }
+
+ public function testAppendsValuesToDescription()
+ {
+ $m = describedAs('value 1 = %0, value 2 = %1', anything(), 33, 97);
+
+ $this->assertDescription('value 1 = <33>, value 2 = <97>', $m);
+ }
+
+ public function testDelegatesMatchingToAnotherMatcher()
+ {
+ $m1 = describedAs('irrelevant', anything());
+ $m2 = describedAs('irrelevant', not(anything()));
+
+ $this->assertTrue($m1->matches(new \stdClass()));
+ $this->assertFalse($m2->matches('hi'));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/EveryTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/EveryTest.php
new file mode 100644
index 0000000..5eb153c
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/EveryTest.php
@@ -0,0 +1,30 @@
+assertEquals('every item is a string containing "a"', (string) $each);
+
+ $this->assertMismatchDescription('an item was "BbB"', $each, array('BbB'));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/HasToStringTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/HasToStringTest.php
new file mode 100644
index 0000000..e2e136d
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/HasToStringTest.php
@@ -0,0 +1,108 @@
+assertMatches(
+ hasToString(equalTo('php')),
+ new \Hamcrest\Core\PhpForm(),
+ 'correct __toString'
+ );
+ $this->assertMatches(
+ hasToString(equalTo('java')),
+ new \Hamcrest\Core\JavaForm(),
+ 'correct toString'
+ );
+ }
+
+ public function testPicksJavaOverPhpToString()
+ {
+ $this->assertMatches(
+ hasToString(equalTo('java')),
+ new \Hamcrest\Core\BothForms(),
+ 'correct toString'
+ );
+ }
+
+ public function testDoesNotMatchWhenToStringDoesNotMatch()
+ {
+ $this->assertDoesNotMatch(
+ hasToString(equalTo('mismatch')),
+ new \Hamcrest\Core\PhpForm(),
+ 'incorrect __toString'
+ );
+ $this->assertDoesNotMatch(
+ hasToString(equalTo('mismatch')),
+ new \Hamcrest\Core\JavaForm(),
+ 'incorrect toString'
+ );
+ $this->assertDoesNotMatch(
+ hasToString(equalTo('mismatch')),
+ new \Hamcrest\Core\BothForms(),
+ 'incorrect __toString'
+ );
+ }
+
+ public function testDoesNotMatchNull()
+ {
+ $this->assertDoesNotMatch(
+ hasToString(equalTo('a')),
+ null,
+ 'should not match null'
+ );
+ }
+
+ public function testProvidesConvenientShortcutForTraversableWithSizeEqualTo()
+ {
+ $this->assertMatches(
+ hasToString(equalTo('php')),
+ new \Hamcrest\Core\PhpForm(),
+ 'correct __toString'
+ );
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription(
+ 'an object with toString() "php"',
+ hasToString(equalTo('php'))
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsAnythingTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsAnythingTest.php
new file mode 100644
index 0000000..f68032e
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsAnythingTest.php
@@ -0,0 +1,29 @@
+assertDescription('ANYTHING', anything());
+ }
+
+ public function testCanOverrideDescription()
+ {
+ $description = 'description';
+ $this->assertDescription($description, anything($description));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsCollectionContainingTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsCollectionContainingTest.php
new file mode 100644
index 0000000..a3929b5
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsCollectionContainingTest.php
@@ -0,0 +1,91 @@
+assertMatches(
+ $itemMatcher,
+ array('a', 'b', 'c'),
+ "should match list that contains 'a'"
+ );
+ }
+
+ public function testDoesNotMatchCollectionThatDoesntContainAnElementMatchingTheGivenMatcher()
+ {
+ $matcher1 = hasItem(equalTo('a'));
+ $this->assertDoesNotMatch(
+ $matcher1,
+ array('b', 'c'),
+ "should not match list that doesn't contain 'a'"
+ );
+
+ $matcher2 = hasItem(equalTo('a'));
+ $this->assertDoesNotMatch(
+ $matcher2,
+ array(),
+ 'should not match the empty list'
+ );
+ }
+
+ public function testDoesNotMatchNull()
+ {
+ $this->assertDoesNotMatch(
+ hasItem(equalTo('a')),
+ null,
+ 'should not match null'
+ );
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription('a collection containing "a"', hasItem(equalTo('a')));
+ }
+
+ public function testMatchesAllItemsInCollection()
+ {
+ $matcher1 = hasItems(equalTo('a'), equalTo('b'), equalTo('c'));
+ $this->assertMatches(
+ $matcher1,
+ array('a', 'b', 'c'),
+ 'should match list containing all items'
+ );
+
+ $matcher2 = hasItems('a', 'b', 'c');
+ $this->assertMatches(
+ $matcher2,
+ array('a', 'b', 'c'),
+ 'should match list containing all items (without matchers)'
+ );
+
+ $matcher3 = hasItems(equalTo('a'), equalTo('b'), equalTo('c'));
+ $this->assertMatches(
+ $matcher3,
+ array('c', 'b', 'a'),
+ 'should match list containing all items in any order'
+ );
+
+ $matcher4 = hasItems(equalTo('a'), equalTo('b'), equalTo('c'));
+ $this->assertMatches(
+ $matcher4,
+ array('e', 'c', 'b', 'a', 'd'),
+ 'should match list containing all items plus others'
+ );
+
+ $matcher5 = hasItems(equalTo('a'), equalTo('b'), equalTo('c'));
+ $this->assertDoesNotMatch(
+ $matcher5,
+ array('e', 'c', 'b', 'd'), // 'a' missing
+ 'should not match list unless it contains all items'
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsEqualTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsEqualTest.php
new file mode 100644
index 0000000..73e3ff0
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsEqualTest.php
@@ -0,0 +1,102 @@
+_arg = $arg;
+ }
+
+ public function __toString()
+ {
+ return $this->_arg;
+ }
+}
+
+class IsEqualTest extends \Hamcrest\AbstractMatcherTest
+{
+
+ protected function createMatcher()
+ {
+ return \Hamcrest\Core\IsEqual::equalTo('irrelevant');
+ }
+
+ public function testComparesObjectsUsingEqualityOperator()
+ {
+ assertThat("hi", equalTo("hi"));
+ assertThat("bye", not(equalTo("hi")));
+
+ assertThat(1, equalTo(1));
+ assertThat(1, not(equalTo(2)));
+
+ assertThat("2", equalTo(2));
+ }
+
+ public function testCanCompareNullValues()
+ {
+ assertThat(null, equalTo(null));
+
+ assertThat(null, not(equalTo('hi')));
+ assertThat('hi', not(equalTo(null)));
+ }
+
+ public function testComparesTheElementsOfAnArray()
+ {
+ $s1 = array('a', 'b');
+ $s2 = array('a', 'b');
+ $s3 = array('c', 'd');
+ $s4 = array('a', 'b', 'c', 'd');
+
+ assertThat($s1, equalTo($s1));
+ assertThat($s2, equalTo($s1));
+ assertThat($s3, not(equalTo($s1)));
+ assertThat($s4, not(equalTo($s1)));
+ }
+
+ public function testComparesTheElementsOfAnArrayOfPrimitiveTypes()
+ {
+ $i1 = array(1, 2);
+ $i2 = array(1, 2);
+ $i3 = array(3, 4);
+ $i4 = array(1, 2, 3, 4);
+
+ assertThat($i1, equalTo($i1));
+ assertThat($i2, equalTo($i1));
+ assertThat($i3, not(equalTo($i1)));
+ assertThat($i4, not(equalTo($i1)));
+ }
+
+ public function testRecursivelyTestsElementsOfArrays()
+ {
+ $i1 = array(array(1, 2), array(3, 4));
+ $i2 = array(array(1, 2), array(3, 4));
+ $i3 = array(array(5, 6), array(7, 8));
+ $i4 = array(array(1, 2, 3, 4), array(3, 4));
+
+ assertThat($i1, equalTo($i1));
+ assertThat($i2, equalTo($i1));
+ assertThat($i3, not(equalTo($i1)));
+ assertThat($i4, not(equalTo($i1)));
+ }
+
+ public function testIncludesTheResultOfCallingToStringOnItsArgumentInTheDescription()
+ {
+ $argumentDescription = 'ARGUMENT DESCRIPTION';
+ $argument = new \Hamcrest\Core\DummyToStringClass($argumentDescription);
+ $this->assertDescription('<' . $argumentDescription . '>', equalTo($argument));
+ }
+
+ public function testReturnsAnObviousDescriptionIfCreatedWithANestedMatcherByMistake()
+ {
+ $innerMatcher = equalTo('NestedMatcher');
+ $this->assertDescription('<' . (string) $innerMatcher . '>', equalTo($innerMatcher));
+ }
+
+ public function testReturnsGoodDescriptionIfCreatedWithNullReference()
+ {
+ $this->assertDescription('null', equalTo(null));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsIdenticalTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsIdenticalTest.php
new file mode 100644
index 0000000..9cc2794
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsIdenticalTest.php
@@ -0,0 +1,30 @@
+assertDescription('"ARG"', identicalTo('ARG'));
+ }
+
+ public function testReturnsReadableDescriptionFromToStringWhenInitialisedWithNull()
+ {
+ $this->assertDescription('null', identicalTo(null));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsInstanceOfTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsInstanceOfTest.php
new file mode 100644
index 0000000..f74cfdb
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsInstanceOfTest.php
@@ -0,0 +1,51 @@
+_baseClassInstance = new \Hamcrest\Core\SampleBaseClass('good');
+ $this->_subClassInstance = new \Hamcrest\Core\SampleSubClass('good');
+ }
+
+ protected function createMatcher()
+ {
+ return \Hamcrest\Core\IsInstanceOf::anInstanceOf('stdClass');
+ }
+
+ public function testEvaluatesToTrueIfArgumentIsInstanceOfASpecificClass()
+ {
+ assertThat($this->_baseClassInstance, anInstanceOf('Hamcrest\Core\SampleBaseClass'));
+ assertThat($this->_subClassInstance, anInstanceOf('Hamcrest\Core\SampleSubClass'));
+ assertThat(null, not(anInstanceOf('Hamcrest\Core\SampleBaseClass')));
+ assertThat(new \stdClass(), not(anInstanceOf('Hamcrest\Core\SampleBaseClass')));
+ }
+
+ public function testEvaluatesToFalseIfArgumentIsNotAnObject()
+ {
+ assertThat(null, not(anInstanceOf('Hamcrest\Core\SampleBaseClass')));
+ assertThat(false, not(anInstanceOf('Hamcrest\Core\SampleBaseClass')));
+ assertThat(5, not(anInstanceOf('Hamcrest\Core\SampleBaseClass')));
+ assertThat('foo', not(anInstanceOf('Hamcrest\Core\SampleBaseClass')));
+ assertThat(array(1, 2, 3), not(anInstanceOf('Hamcrest\Core\SampleBaseClass')));
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription('an instance of stdClass', anInstanceOf('stdClass'));
+ }
+
+ public function testDecribesActualClassInMismatchMessage()
+ {
+ $this->assertMismatchDescription(
+ '[Hamcrest\Core\SampleBaseClass] ',
+ anInstanceOf('Hamcrest\Core\SampleSubClass'),
+ $this->_baseClassInstance
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsNotTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsNotTest.php
new file mode 100644
index 0000000..09d4a65
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsNotTest.php
@@ -0,0 +1,31 @@
+assertMatches(not(equalTo('A')), 'B', 'should match');
+ $this->assertDoesNotMatch(not(equalTo('B')), 'B', 'should not match');
+ }
+
+ public function testProvidesConvenientShortcutForNotEqualTo()
+ {
+ $this->assertMatches(not('A'), 'B', 'should match');
+ $this->assertMatches(not('B'), 'A', 'should match');
+ $this->assertDoesNotMatch(not('A'), 'A', 'should not match');
+ $this->assertDoesNotMatch(not('B'), 'B', 'should not match');
+ }
+
+ public function testUsesDescriptionOfNegatedMatcherWithPrefix()
+ {
+ $this->assertDescription('not a value greater than <2>', not(greaterThan(2)));
+ $this->assertDescription('not "A"', not('A'));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsNullTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsNullTest.php
new file mode 100644
index 0000000..bfa4255
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsNullTest.php
@@ -0,0 +1,20 @@
+assertDescription('sameInstance("ARG")', sameInstance('ARG'));
+ }
+
+ public function testReturnsReadableDescriptionFromToStringWhenInitialisedWithNull()
+ {
+ $this->assertDescription('sameInstance(null)', sameInstance(null));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsTest.php
new file mode 100644
index 0000000..bbd848b
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsTest.php
@@ -0,0 +1,33 @@
+assertMatches(is(equalTo(true)), true, 'should match');
+ $this->assertMatches(is(equalTo(false)), false, 'should match');
+ $this->assertDoesNotMatch(is(equalTo(true)), false, 'should not match');
+ $this->assertDoesNotMatch(is(equalTo(false)), true, 'should not match');
+ }
+
+ public function testGeneratesIsPrefixInDescription()
+ {
+ $this->assertDescription('is ', is(equalTo(true)));
+ }
+
+ public function testProvidesConvenientShortcutForIsEqualTo()
+ {
+ $this->assertMatches(is('A'), 'A', 'should match');
+ $this->assertMatches(is('B'), 'B', 'should match');
+ $this->assertDoesNotMatch(is('A'), 'B', 'should not match');
+ $this->assertDoesNotMatch(is('B'), 'A', 'should not match');
+ $this->assertDescription('is "A"', is('A'));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsTypeOfTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsTypeOfTest.php
new file mode 100644
index 0000000..3f48dea
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/IsTypeOfTest.php
@@ -0,0 +1,45 @@
+assertDescription('a double', typeOf('double'));
+ $this->assertDescription('an integer', typeOf('integer'));
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', typeOf('boolean'), null);
+ $this->assertMismatchDescription('was an integer <5>', typeOf('float'), 5);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/SampleBaseClass.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/SampleBaseClass.php
new file mode 100644
index 0000000..c953e7c
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/SampleBaseClass.php
@@ -0,0 +1,18 @@
+_arg = $arg;
+ }
+
+ public function __toString()
+ {
+ return $this->_arg;
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/SampleSubClass.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/SampleSubClass.php
new file mode 100644
index 0000000..822f1b6
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Core/SampleSubClass.php
@@ -0,0 +1,6 @@
+_instanceProperty);
+ }
+
+ protected function createMatcher()
+ {
+ return \Hamcrest\Core\Set::set('property_name');
+ }
+
+ public function testEvaluatesToTrueIfArrayPropertyIsSet()
+ {
+ assertThat(array('foo' => 'bar'), set('foo'));
+ }
+
+ public function testNegatedEvaluatesToFalseIfArrayPropertyIsSet()
+ {
+ assertThat(array('foo' => 'bar'), not(notSet('foo')));
+ }
+
+ public function testEvaluatesToTrueIfClassPropertyIsSet()
+ {
+ self::$_classProperty = 'bar';
+ assertThat('Hamcrest\Core\SetTest', set('_classProperty'));
+ }
+
+ public function testNegatedEvaluatesToFalseIfClassPropertyIsSet()
+ {
+ self::$_classProperty = 'bar';
+ assertThat('Hamcrest\Core\SetTest', not(notSet('_classProperty')));
+ }
+
+ public function testEvaluatesToTrueIfObjectPropertyIsSet()
+ {
+ $this->_instanceProperty = 'bar';
+ assertThat($this, set('_instanceProperty'));
+ }
+
+ public function testNegatedEvaluatesToFalseIfObjectPropertyIsSet()
+ {
+ $this->_instanceProperty = 'bar';
+ assertThat($this, not(notSet('_instanceProperty')));
+ }
+
+ public function testEvaluatesToFalseIfArrayPropertyIsNotSet()
+ {
+ assertThat(array('foo' => 'bar'), not(set('baz')));
+ }
+
+ public function testNegatedEvaluatesToTrueIfArrayPropertyIsNotSet()
+ {
+ assertThat(array('foo' => 'bar'), notSet('baz'));
+ }
+
+ public function testEvaluatesToFalseIfClassPropertyIsNotSet()
+ {
+ assertThat('Hamcrest\Core\SetTest', not(set('_classProperty')));
+ }
+
+ public function testNegatedEvaluatesToTrueIfClassPropertyIsNotSet()
+ {
+ assertThat('Hamcrest\Core\SetTest', notSet('_classProperty'));
+ }
+
+ public function testEvaluatesToFalseIfObjectPropertyIsNotSet()
+ {
+ assertThat($this, not(set('_instanceProperty')));
+ }
+
+ public function testNegatedEvaluatesToTrueIfObjectPropertyIsNotSet()
+ {
+ assertThat($this, notSet('_instanceProperty'));
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription('set property foo', set('foo'));
+ $this->assertDescription('unset property bar', notSet('bar'));
+ }
+
+ public function testDecribesPropertySettingInMismatchMessage()
+ {
+ $this->assertMismatchDescription(
+ 'was not set',
+ set('bar'),
+ array('foo' => 'bar')
+ );
+ $this->assertMismatchDescription(
+ 'was "bar"',
+ notSet('foo'),
+ array('foo' => 'bar')
+ );
+ self::$_classProperty = 'bar';
+ $this->assertMismatchDescription(
+ 'was "bar"',
+ notSet('_classProperty'),
+ 'Hamcrest\Core\SetTest'
+ );
+ $this->_instanceProperty = 'bar';
+ $this->assertMismatchDescription(
+ 'was "bar"',
+ notSet('_instanceProperty'),
+ $this
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/FeatureMatcherTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/FeatureMatcherTest.php
new file mode 100644
index 0000000..1b02304
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/FeatureMatcherTest.php
@@ -0,0 +1,73 @@
+_result = $result;
+ }
+ public function getResult()
+ {
+ return $this->_result;
+ }
+}
+
+/* Test-specific subclass only */
+class ResultMatcher extends \Hamcrest\FeatureMatcher
+{
+ public function __construct()
+ {
+ parent::__construct(self::TYPE_ANY, null, equalTo('bar'), 'Thingy with result', 'result');
+ }
+ public function featureValueOf($actual)
+ {
+ if ($actual instanceof \Hamcrest\Thingy) {
+ return $actual->getResult();
+ }
+ }
+}
+
+class FeatureMatcherTest extends \Hamcrest\AbstractMatcherTest
+{
+
+ private $_resultMatcher;
+
+ protected function setUp()
+ {
+ $this->_resultMatcher = $this->_resultMatcher();
+ }
+
+ protected function createMatcher()
+ {
+ return $this->_resultMatcher();
+ }
+
+ public function testMatchesPartOfAnObject()
+ {
+ $this->assertMatches($this->_resultMatcher, new \Hamcrest\Thingy('bar'), 'feature');
+ $this->assertDescription('Thingy with result "bar"', $this->_resultMatcher);
+ }
+
+ public function testMismatchesPartOfAnObject()
+ {
+ $this->assertMismatchDescription(
+ 'result was "foo"',
+ $this->_resultMatcher,
+ new \Hamcrest\Thingy('foo')
+ );
+ }
+
+ public function testDoesNotGenerateNoticesForNull()
+ {
+ $this->assertMismatchDescription('result was null', $this->_resultMatcher, null);
+ }
+
+ // -- Creation Methods
+
+ private function _resultMatcher()
+ {
+ return new \Hamcrest\ResultMatcher();
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/InvokedMatcherTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/InvokedMatcherTest.php
new file mode 100644
index 0000000..dfa7700
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/InvokedMatcherTest.php
@@ -0,0 +1,31 @@
+matchAgainst = $matchAgainst;
+ }
+
+ public function matches($item)
+ {
+ return $item == $this->matchAgainst;
+ }
+
+}
+
+class InvokedMatcherTest extends TestCase
+{
+ public function testInvokedMatchersCallMatches()
+ {
+ $sampleMatcher = new SampleInvokeMatcher('foo');
+
+ $this->assertTrue($sampleMatcher('foo'));
+ $this->assertFalse($sampleMatcher('bar'));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/MatcherAssertTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/MatcherAssertTest.php
new file mode 100644
index 0000000..dc12fba
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/MatcherAssertTest.php
@@ -0,0 +1,192 @@
+getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat(null);
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('', $ex->getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat('');
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('', $ex->getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat(0);
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('', $ex->getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat(0.0);
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('', $ex->getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat(array());
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('', $ex->getMessage());
+ }
+ self::assertEquals(6, \Hamcrest\MatcherAssert::getCount(), 'assertion count');
+ }
+
+ public function testAssertThatWithIdentifierAndTrueArgPasses()
+ {
+ \Hamcrest\MatcherAssert::assertThat('identifier', true);
+ \Hamcrest\MatcherAssert::assertThat('identifier', 'non-empty');
+ \Hamcrest\MatcherAssert::assertThat('identifier', 1);
+ \Hamcrest\MatcherAssert::assertThat('identifier', 3.14159);
+ \Hamcrest\MatcherAssert::assertThat('identifier', array(true));
+ self::assertEquals(5, \Hamcrest\MatcherAssert::getCount(), 'assertion count');
+ }
+
+ public function testAssertThatWithIdentifierAndFalseArgFails()
+ {
+ try {
+ \Hamcrest\MatcherAssert::assertThat('identifier', false);
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('identifier', $ex->getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat('identifier', null);
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('identifier', $ex->getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat('identifier', '');
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('identifier', $ex->getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat('identifier', 0);
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('identifier', $ex->getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat('identifier', 0.0);
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('identifier', $ex->getMessage());
+ }
+ try {
+ \Hamcrest\MatcherAssert::assertThat('identifier', array());
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals('identifier', $ex->getMessage());
+ }
+ self::assertEquals(6, \Hamcrest\MatcherAssert::getCount(), 'assertion count');
+ }
+
+ public function testAssertThatWithActualValueAndMatcherArgsThatMatchPasses()
+ {
+ \Hamcrest\MatcherAssert::assertThat(true, is(true));
+ self::assertEquals(1, \Hamcrest\MatcherAssert::getCount(), 'assertion count');
+ }
+
+ public function testAssertThatWithActualValueAndMatcherArgsThatDontMatchFails()
+ {
+ $expected = 'expected';
+ $actual = 'actual';
+
+ $expectedMessage =
+ 'Expected: "expected"' . PHP_EOL .
+ ' but: was "actual"';
+
+ try {
+ \Hamcrest\MatcherAssert::assertThat($actual, equalTo($expected));
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals($expectedMessage, $ex->getMessage());
+ self::assertEquals(1, \Hamcrest\MatcherAssert::getCount(), 'assertion count');
+ }
+ }
+
+ public function testAssertThatWithIdentifierAndActualValueAndMatcherArgsThatMatchPasses()
+ {
+ \Hamcrest\MatcherAssert::assertThat('identifier', true, is(true));
+ self::assertEquals(1, \Hamcrest\MatcherAssert::getCount(), 'assertion count');
+ }
+
+ public function testAssertThatWithIdentifierAndActualValueAndMatcherArgsThatDontMatchFails()
+ {
+ $expected = 'expected';
+ $actual = 'actual';
+
+ $expectedMessage =
+ 'identifier' . PHP_EOL .
+ 'Expected: "expected"' . PHP_EOL .
+ ' but: was "actual"';
+
+ try {
+ \Hamcrest\MatcherAssert::assertThat('identifier', $actual, equalTo($expected));
+ self::fail('expected assertion failure');
+ } catch (\Hamcrest\AssertionError $ex) {
+ self::assertEquals($expectedMessage, $ex->getMessage());
+ self::assertEquals(1, \Hamcrest\MatcherAssert::getCount(), 'assertion count');
+ }
+ }
+
+ public function testAssertThatWithNoArgsThrowsErrorAndDoesntIncrementCount()
+ {
+ try {
+ \Hamcrest\MatcherAssert::assertThat();
+ self::fail('expected invalid argument exception');
+ } catch (\InvalidArgumentException $ex) {
+ self::assertEquals(0, \Hamcrest\MatcherAssert::getCount(), 'assertion count');
+ }
+ }
+
+ public function testAssertThatWithFourArgsThrowsErrorAndDoesntIncrementCount()
+ {
+ try {
+ \Hamcrest\MatcherAssert::assertThat(1, 2, 3, 4);
+ self::fail('expected invalid argument exception');
+ } catch (\InvalidArgumentException $ex) {
+ self::assertEquals(0, \Hamcrest\MatcherAssert::getCount(), 'assertion count');
+ }
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Number/IsCloseToTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Number/IsCloseToTest.php
new file mode 100644
index 0000000..987d552
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Number/IsCloseToTest.php
@@ -0,0 +1,27 @@
+assertTrue($p->matches(1.0));
+ $this->assertTrue($p->matches(0.5));
+ $this->assertTrue($p->matches(1.5));
+
+ $this->assertDoesNotMatch($p, 2.0, 'too large');
+ $this->assertMismatchDescription('<2F> differed by <0.5F>', $p, 2.0);
+ $this->assertDoesNotMatch($p, 0.0, 'number too small');
+ $this->assertMismatchDescription('<0F> differed by <0.5F>', $p, 0.0);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Number/OrderingComparisonTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Number/OrderingComparisonTest.php
new file mode 100644
index 0000000..a4c94d3
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Number/OrderingComparisonTest.php
@@ -0,0 +1,41 @@
+_text = $text;
+ }
+
+ public function describeTo(\Hamcrest\Description $description)
+ {
+ $description->appendText($this->_text);
+ }
+}
+
+class StringDescriptionTest extends TestCase
+{
+
+ private $_description;
+
+ protected function setUp()
+ {
+ $this->_description = new \Hamcrest\StringDescription();
+ }
+
+ public function testAppendTextAppendsTextInformation()
+ {
+ $this->_description->appendText('foo')->appendText('bar');
+ $this->assertEquals('foobar', (string) $this->_description);
+ }
+
+ public function testAppendValueCanAppendTextTypes()
+ {
+ $this->_description->appendValue('foo');
+ $this->assertEquals('"foo"', (string) $this->_description);
+ }
+
+ public function testSpecialCharactersAreEscapedForStringTypes()
+ {
+ $this->_description->appendValue("foo\\bar\"zip\r\n");
+ $this->assertEquals('"foo\\bar\\"zip\r\n"', (string) $this->_description);
+ }
+
+ public function testIntegerValuesCanBeAppended()
+ {
+ $this->_description->appendValue(42);
+ $this->assertEquals('<42>', (string) $this->_description);
+ }
+
+ public function testFloatValuesCanBeAppended()
+ {
+ $this->_description->appendValue(42.78);
+ $this->assertEquals('<42.78F>', (string) $this->_description);
+ }
+
+ public function testNullValuesCanBeAppended()
+ {
+ $this->_description->appendValue(null);
+ $this->assertEquals('null', (string) $this->_description);
+ }
+
+ public function testArraysCanBeAppended()
+ {
+ $this->_description->appendValue(array('foo', 42.78));
+ $this->assertEquals('["foo", <42.78F>]', (string) $this->_description);
+ }
+
+ public function testObjectsCanBeAppended()
+ {
+ $this->_description->appendValue(new \stdClass());
+ $this->assertEquals('', (string) $this->_description);
+ }
+
+ public function testBooleanValuesCanBeAppended()
+ {
+ $this->_description->appendValue(false);
+ $this->assertEquals('', (string) $this->_description);
+ }
+
+ public function testListsOfvaluesCanBeAppended()
+ {
+ $this->_description->appendValue(array('foo', 42.78));
+ $this->assertEquals('["foo", <42.78F>]', (string) $this->_description);
+ }
+
+ public function testIterableOfvaluesCanBeAppended()
+ {
+ $items = new \ArrayObject(array('foo', 42.78));
+ $this->_description->appendValue($items);
+ $this->assertEquals('["foo", <42.78F>]', (string) $this->_description);
+ }
+
+ public function testIteratorOfvaluesCanBeAppended()
+ {
+ $items = new \ArrayObject(array('foo', 42.78));
+ $this->_description->appendValue($items->getIterator());
+ $this->assertEquals('["foo", <42.78F>]', (string) $this->_description);
+ }
+
+ public function testListsOfvaluesCanBeAppendedManually()
+ {
+ $this->_description->appendValueList('@start@', '@sep@ ', '@end@', array('foo', 42.78));
+ $this->assertEquals('@start@"foo"@sep@ <42.78F>@end@', (string) $this->_description);
+ }
+
+ public function testIterableOfvaluesCanBeAppendedManually()
+ {
+ $items = new \ArrayObject(array('foo', 42.78));
+ $this->_description->appendValueList('@start@', '@sep@ ', '@end@', $items);
+ $this->assertEquals('@start@"foo"@sep@ <42.78F>@end@', (string) $this->_description);
+ }
+
+ public function testIteratorOfvaluesCanBeAppendedManually()
+ {
+ $items = new \ArrayObject(array('foo', 42.78));
+ $this->_description->appendValueList('@start@', '@sep@ ', '@end@', $items->getIterator());
+ $this->assertEquals('@start@"foo"@sep@ <42.78F>@end@', (string) $this->_description);
+ }
+
+ public function testSelfDescribingObjectsCanBeAppended()
+ {
+ $this->_description
+ ->appendDescriptionOf(new \Hamcrest\SampleSelfDescriber('foo'))
+ ->appendDescriptionOf(new \Hamcrest\SampleSelfDescriber('bar'))
+ ;
+ $this->assertEquals('foobar', (string) $this->_description);
+ }
+
+ public function testSelfDescribingObjectsCanBeAppendedAsLists()
+ {
+ $this->_description->appendList('@start@', '@sep@ ', '@end@', array(
+ new \Hamcrest\SampleSelfDescriber('foo'),
+ new \Hamcrest\SampleSelfDescriber('bar')
+ ));
+ $this->assertEquals('@start@foo@sep@ bar@end@', (string) $this->_description);
+ }
+
+ public function testSelfDescribingObjectsCanBeAppendedAsIteratedLists()
+ {
+ $items = new \ArrayObject(array(
+ new \Hamcrest\SampleSelfDescriber('foo'),
+ new \Hamcrest\SampleSelfDescriber('bar')
+ ));
+ $this->_description->appendList('@start@', '@sep@ ', '@end@', $items);
+ $this->assertEquals('@start@foo@sep@ bar@end@', (string) $this->_description);
+ }
+
+ public function testSelfDescribingObjectsCanBeAppendedAsIterators()
+ {
+ $items = new \ArrayObject(array(
+ new \Hamcrest\SampleSelfDescriber('foo'),
+ new \Hamcrest\SampleSelfDescriber('bar')
+ ));
+ $this->_description->appendList('@start@', '@sep@ ', '@end@', $items->getIterator());
+ $this->assertEquals('@start@foo@sep@ bar@end@', (string) $this->_description);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/IsEmptyStringTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/IsEmptyStringTest.php
new file mode 100644
index 0000000..8d5c56b
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/IsEmptyStringTest.php
@@ -0,0 +1,86 @@
+assertDoesNotMatch(emptyString(), null, 'null');
+ }
+
+ public function testEmptyDoesNotMatchZero()
+ {
+ $this->assertDoesNotMatch(emptyString(), 0, 'zero');
+ }
+
+ public function testEmptyDoesNotMatchFalse()
+ {
+ $this->assertDoesNotMatch(emptyString(), false, 'false');
+ }
+
+ public function testEmptyDoesNotMatchEmptyArray()
+ {
+ $this->assertDoesNotMatch(emptyString(), array(), 'empty array');
+ }
+
+ public function testEmptyMatchesEmptyString()
+ {
+ $this->assertMatches(emptyString(), '', 'empty string');
+ }
+
+ public function testEmptyDoesNotMatchNonEmptyString()
+ {
+ $this->assertDoesNotMatch(emptyString(), 'foo', 'non-empty string');
+ }
+
+ public function testEmptyHasAReadableDescription()
+ {
+ $this->assertDescription('an empty string', emptyString());
+ }
+
+ public function testEmptyOrNullMatchesNull()
+ {
+ $this->assertMatches(nullOrEmptyString(), null, 'null');
+ }
+
+ public function testEmptyOrNullMatchesEmptyString()
+ {
+ $this->assertMatches(nullOrEmptyString(), '', 'empty string');
+ }
+
+ public function testEmptyOrNullDoesNotMatchNonEmptyString()
+ {
+ $this->assertDoesNotMatch(nullOrEmptyString(), 'foo', 'non-empty string');
+ }
+
+ public function testEmptyOrNullHasAReadableDescription()
+ {
+ $this->assertDescription('(null or an empty string)', nullOrEmptyString());
+ }
+
+ public function testNonEmptyDoesNotMatchNull()
+ {
+ $this->assertDoesNotMatch(nonEmptyString(), null, 'null');
+ }
+
+ public function testNonEmptyDoesNotMatchEmptyString()
+ {
+ $this->assertDoesNotMatch(nonEmptyString(), '', 'empty string');
+ }
+
+ public function testNonEmptyMatchesNonEmptyString()
+ {
+ $this->assertMatches(nonEmptyString(), 'foo', 'non-empty string');
+ }
+
+ public function testNonEmptyHasAReadableDescription()
+ {
+ $this->assertDescription('a non-empty string', nonEmptyString());
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/IsEqualIgnoringCaseTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/IsEqualIgnoringCaseTest.php
new file mode 100644
index 0000000..0539fd5
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/IsEqualIgnoringCaseTest.php
@@ -0,0 +1,40 @@
+assertDescription(
+ 'equalToIgnoringCase("heLLo")',
+ equalToIgnoringCase('heLLo')
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/IsEqualIgnoringWhiteSpaceTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/IsEqualIgnoringWhiteSpaceTest.php
new file mode 100644
index 0000000..27ad338
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/IsEqualIgnoringWhiteSpaceTest.php
@@ -0,0 +1,51 @@
+_matcher = \Hamcrest\Text\IsEqualIgnoringWhiteSpace::equalToIgnoringWhiteSpace(
+ "Hello World how\n are we? "
+ );
+ }
+
+ protected function createMatcher()
+ {
+ return $this->_matcher;
+ }
+
+ public function testPassesIfWordsAreSameButWhitespaceDiffers()
+ {
+ assertThat('Hello World how are we?', $this->_matcher);
+ assertThat(" Hello \rWorld \t how are\nwe?", $this->_matcher);
+ }
+
+ public function testFailsIfTextOtherThanWhitespaceDiffers()
+ {
+ assertThat('Hello PLANET how are we?', not($this->_matcher));
+ assertThat('Hello World how are we', not($this->_matcher));
+ }
+
+ public function testFailsIfWhitespaceIsAddedOrRemovedInMidWord()
+ {
+ assertThat('HelloWorld how are we?', not($this->_matcher));
+ assertThat('Hello Wo rld how are we?', not($this->_matcher));
+ }
+
+ public function testFailsIfMatchingAgainstNull()
+ {
+ assertThat(null, not($this->_matcher));
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription(
+ "equalToIgnoringWhiteSpace(\"Hello World how\\n are we? \")",
+ $this->_matcher
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/MatchesPatternTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/MatchesPatternTest.php
new file mode 100644
index 0000000..4891598
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/MatchesPatternTest.php
@@ -0,0 +1,30 @@
+assertDescription('a string matching "pattern"', matchesPattern('pattern'));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringContainsIgnoringCaseTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringContainsIgnoringCaseTest.php
new file mode 100644
index 0000000..7302300
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringContainsIgnoringCaseTest.php
@@ -0,0 +1,80 @@
+_stringContains = \Hamcrest\Text\StringContainsIgnoringCase::containsStringIgnoringCase(
+ strtolower(self::EXCERPT)
+ );
+ }
+
+ protected function createMatcher()
+ {
+ return $this->_stringContains;
+ }
+
+ public function testEvaluatesToTrueIfArgumentContainsSpecifiedSubstring()
+ {
+ $this->assertTrue(
+ $this->_stringContains->matches(self::EXCERPT . 'END'),
+ 'should be true if excerpt at beginning'
+ );
+ $this->assertTrue(
+ $this->_stringContains->matches('START' . self::EXCERPT),
+ 'should be true if excerpt at end'
+ );
+ $this->assertTrue(
+ $this->_stringContains->matches('START' . self::EXCERPT . 'END'),
+ 'should be true if excerpt in middle'
+ );
+ $this->assertTrue(
+ $this->_stringContains->matches(self::EXCERPT . self::EXCERPT),
+ 'should be true if excerpt is repeated'
+ );
+
+ $this->assertFalse(
+ $this->_stringContains->matches('Something else'),
+ 'should not be true if excerpt is not in string'
+ );
+ $this->assertFalse(
+ $this->_stringContains->matches(substr(self::EXCERPT, 1)),
+ 'should not be true if part of excerpt is in string'
+ );
+ }
+
+ public function testEvaluatesToTrueIfArgumentIsEqualToSubstring()
+ {
+ $this->assertTrue(
+ $this->_stringContains->matches(self::EXCERPT),
+ 'should be true if excerpt is entire string'
+ );
+ }
+
+ public function testEvaluatesToTrueIfArgumentContainsExactSubstring()
+ {
+ $this->assertTrue(
+ $this->_stringContains->matches(strtolower(self::EXCERPT)),
+ 'should be false if excerpt is entire string ignoring case'
+ );
+ $this->assertTrue(
+ $this->_stringContains->matches('START' . strtolower(self::EXCERPT) . 'END'),
+ 'should be false if excerpt is contained in string ignoring case'
+ );
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription(
+ 'a string containing in any case "'
+ . strtolower(self::EXCERPT) . '"',
+ $this->_stringContains
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringContainsInOrderTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringContainsInOrderTest.php
new file mode 100644
index 0000000..4c465b2
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringContainsInOrderTest.php
@@ -0,0 +1,42 @@
+_m = \Hamcrest\Text\StringContainsInOrder::stringContainsInOrder(array('a', 'b', 'c'));
+ }
+
+ protected function createMatcher()
+ {
+ return $this->_m;
+ }
+
+ public function testMatchesOnlyIfStringContainsGivenSubstringsInTheSameOrder()
+ {
+ $this->assertMatches($this->_m, 'abc', 'substrings in order');
+ $this->assertMatches($this->_m, '1a2b3c4', 'substrings separated');
+
+ $this->assertDoesNotMatch($this->_m, 'cab', 'substrings out of order');
+ $this->assertDoesNotMatch($this->_m, 'xyz', 'no substrings in string');
+ $this->assertDoesNotMatch($this->_m, 'ac', 'substring missing');
+ $this->assertDoesNotMatch($this->_m, '', 'empty string');
+ }
+
+ public function testAcceptsVariableArguments()
+ {
+ $this->assertMatches(stringContainsInOrder('a', 'b', 'c'), 'abc', 'substrings as variable arguments');
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription(
+ 'a string containing "a", "b", "c" in order',
+ $this->_m
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringContainsTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringContainsTest.php
new file mode 100644
index 0000000..bf4afa3
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringContainsTest.php
@@ -0,0 +1,86 @@
+_stringContains = \Hamcrest\Text\StringContains::containsString(self::EXCERPT);
+ }
+
+ protected function createMatcher()
+ {
+ return $this->_stringContains;
+ }
+
+ public function testEvaluatesToTrueIfArgumentContainsSubstring()
+ {
+ $this->assertTrue(
+ $this->_stringContains->matches(self::EXCERPT . 'END'),
+ 'should be true if excerpt at beginning'
+ );
+ $this->assertTrue(
+ $this->_stringContains->matches('START' . self::EXCERPT),
+ 'should be true if excerpt at end'
+ );
+ $this->assertTrue(
+ $this->_stringContains->matches('START' . self::EXCERPT . 'END'),
+ 'should be true if excerpt in middle'
+ );
+ $this->assertTrue(
+ $this->_stringContains->matches(self::EXCERPT . self::EXCERPT),
+ 'should be true if excerpt is repeated'
+ );
+
+ $this->assertFalse(
+ $this->_stringContains->matches('Something else'),
+ 'should not be true if excerpt is not in string'
+ );
+ $this->assertFalse(
+ $this->_stringContains->matches(substr(self::EXCERPT, 1)),
+ 'should not be true if part of excerpt is in string'
+ );
+ }
+
+ public function testEvaluatesToTrueIfArgumentIsEqualToSubstring()
+ {
+ $this->assertTrue(
+ $this->_stringContains->matches(self::EXCERPT),
+ 'should be true if excerpt is entire string'
+ );
+ }
+
+ public function testEvaluatesToFalseIfArgumentContainsSubstringIgnoringCase()
+ {
+ $this->assertFalse(
+ $this->_stringContains->matches(strtolower(self::EXCERPT)),
+ 'should be false if excerpt is entire string ignoring case'
+ );
+ $this->assertFalse(
+ $this->_stringContains->matches('START' . strtolower(self::EXCERPT) . 'END'),
+ 'should be false if excerpt is contained in string ignoring case'
+ );
+ }
+
+ public function testIgnoringCaseReturnsCorrectMatcher()
+ {
+ $this->assertTrue(
+ $this->_stringContains->ignoringCase()->matches('EXceRpT'),
+ 'should be true if excerpt is entire string ignoring case'
+ );
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription(
+ 'a string containing "'
+ . self::EXCERPT . '"',
+ $this->_stringContains
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringEndsWithTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringEndsWithTest.php
new file mode 100644
index 0000000..9a30f95
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringEndsWithTest.php
@@ -0,0 +1,62 @@
+_stringEndsWith = \Hamcrest\Text\StringEndsWith::endsWith(self::EXCERPT);
+ }
+
+ protected function createMatcher()
+ {
+ return $this->_stringEndsWith;
+ }
+
+ public function testEvaluatesToTrueIfArgumentContainsSpecifiedSubstring()
+ {
+ $this->assertFalse(
+ $this->_stringEndsWith->matches(self::EXCERPT . 'END'),
+ 'should be false if excerpt at beginning'
+ );
+ $this->assertTrue(
+ $this->_stringEndsWith->matches('START' . self::EXCERPT),
+ 'should be true if excerpt at end'
+ );
+ $this->assertFalse(
+ $this->_stringEndsWith->matches('START' . self::EXCERPT . 'END'),
+ 'should be false if excerpt in middle'
+ );
+ $this->assertTrue(
+ $this->_stringEndsWith->matches(self::EXCERPT . self::EXCERPT),
+ 'should be true if excerpt is at end and repeated'
+ );
+
+ $this->assertFalse(
+ $this->_stringEndsWith->matches('Something else'),
+ 'should be false if excerpt is not in string'
+ );
+ $this->assertFalse(
+ $this->_stringEndsWith->matches(substr(self::EXCERPT, 0, strlen(self::EXCERPT) - 2)),
+ 'should be false if part of excerpt is at end of string'
+ );
+ }
+
+ public function testEvaluatesToTrueIfArgumentIsEqualToSubstring()
+ {
+ $this->assertTrue(
+ $this->_stringEndsWith->matches(self::EXCERPT),
+ 'should be true if excerpt is entire string'
+ );
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription('a string ending with "EXCERPT"', $this->_stringEndsWith);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringStartsWithTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringStartsWithTest.php
new file mode 100644
index 0000000..3be201f
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Text/StringStartsWithTest.php
@@ -0,0 +1,62 @@
+_stringStartsWith = \Hamcrest\Text\StringStartsWith::startsWith(self::EXCERPT);
+ }
+
+ protected function createMatcher()
+ {
+ return $this->_stringStartsWith;
+ }
+
+ public function testEvaluatesToTrueIfArgumentContainsSpecifiedSubstring()
+ {
+ $this->assertTrue(
+ $this->_stringStartsWith->matches(self::EXCERPT . 'END'),
+ 'should be true if excerpt at beginning'
+ );
+ $this->assertFalse(
+ $this->_stringStartsWith->matches('START' . self::EXCERPT),
+ 'should be false if excerpt at end'
+ );
+ $this->assertFalse(
+ $this->_stringStartsWith->matches('START' . self::EXCERPT . 'END'),
+ 'should be false if excerpt in middle'
+ );
+ $this->assertTrue(
+ $this->_stringStartsWith->matches(self::EXCERPT . self::EXCERPT),
+ 'should be true if excerpt is at beginning and repeated'
+ );
+
+ $this->assertFalse(
+ $this->_stringStartsWith->matches('Something else'),
+ 'should be false if excerpt is not in string'
+ );
+ $this->assertFalse(
+ $this->_stringStartsWith->matches(substr(self::EXCERPT, 1)),
+ 'should be false if part of excerpt is at start of string'
+ );
+ }
+
+ public function testEvaluatesToTrueIfArgumentIsEqualToSubstring()
+ {
+ $this->assertTrue(
+ $this->_stringStartsWith->matches(self::EXCERPT),
+ 'should be true if excerpt is entire string'
+ );
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription('a string starting with "EXCERPT"', $this->_stringStartsWith);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsArrayTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsArrayTest.php
new file mode 100644
index 0000000..d13c24d
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsArrayTest.php
@@ -0,0 +1,35 @@
+assertDescription('an array', arrayValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', arrayValue(), null);
+ $this->assertMismatchDescription('was a string "foo"', arrayValue(), 'foo');
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsBooleanTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsBooleanTest.php
new file mode 100644
index 0000000..24309fc
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsBooleanTest.php
@@ -0,0 +1,35 @@
+assertDescription('a boolean', booleanValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', booleanValue(), null);
+ $this->assertMismatchDescription('was a string "foo"', booleanValue(), 'foo');
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsCallableTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsCallableTest.php
new file mode 100644
index 0000000..5098e21
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsCallableTest.php
@@ -0,0 +1,103 @@
+=')) {
+ $this->markTestSkipped('Closures require php 5.3');
+ }
+ eval('assertThat(function () {}, callableValue());');
+ }
+
+ public function testEvaluatesToTrueIfArgumentImplementsInvoke()
+ {
+ if (!version_compare(PHP_VERSION, '5.3', '>=')) {
+ $this->markTestSkipped('Magic method __invoke() requires php 5.3');
+ }
+ assertThat($this, callableValue());
+ }
+
+ public function testEvaluatesToFalseIfArgumentIsInvalidFunctionName()
+ {
+ if (function_exists('not_a_Hamcrest_function')) {
+ $this->markTestSkipped('Function "not_a_Hamcrest_function" must not exist');
+ }
+
+ assertThat('not_a_Hamcrest_function', not(callableValue()));
+ }
+
+ public function testEvaluatesToFalseIfArgumentIsInvalidStaticMethodCallback()
+ {
+ assertThat(
+ array('Hamcrest\Type\IsCallableTest', 'noMethod'),
+ not(callableValue())
+ );
+ }
+
+ public function testEvaluatesToFalseIfArgumentIsInvalidInstanceMethodCallback()
+ {
+ assertThat(array($this, 'noMethod'), not(callableValue()));
+ }
+
+ public function testEvaluatesToFalseIfArgumentDoesntImplementInvoke()
+ {
+ assertThat(new \stdClass(), not(callableValue()));
+ }
+
+ public function testEvaluatesToFalseIfArgumentDoesntMatchType()
+ {
+ assertThat(false, not(callableValue()));
+ assertThat(5.2, not(callableValue()));
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription('a callable', callableValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription(
+ 'was a string "invalid-function"',
+ callableValue(),
+ 'invalid-function'
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsDoubleTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsDoubleTest.php
new file mode 100644
index 0000000..85c2a96
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsDoubleTest.php
@@ -0,0 +1,35 @@
+assertDescription('a double', doubleValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', doubleValue(), null);
+ $this->assertMismatchDescription('was a string "foo"', doubleValue(), 'foo');
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsIntegerTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsIntegerTest.php
new file mode 100644
index 0000000..ce5a51a
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsIntegerTest.php
@@ -0,0 +1,36 @@
+assertDescription('an integer', integerValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', integerValue(), null);
+ $this->assertMismatchDescription('was a string "foo"', integerValue(), 'foo');
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsNumericTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsNumericTest.php
new file mode 100644
index 0000000..1fd83ef
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsNumericTest.php
@@ -0,0 +1,53 @@
+assertDescription('a number', numericValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', numericValue(), null);
+ $this->assertMismatchDescription('was a string "foo"', numericValue(), 'foo');
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsObjectTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsObjectTest.php
new file mode 100644
index 0000000..a3b617c
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsObjectTest.php
@@ -0,0 +1,34 @@
+assertDescription('an object', objectValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', objectValue(), null);
+ $this->assertMismatchDescription('was a string "foo"', objectValue(), 'foo');
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsResourceTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsResourceTest.php
new file mode 100644
index 0000000..d6ea534
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsResourceTest.php
@@ -0,0 +1,34 @@
+assertDescription('a resource', resourceValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', resourceValue(), null);
+ $this->assertMismatchDescription('was a string "foo"', resourceValue(), 'foo');
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsScalarTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsScalarTest.php
new file mode 100644
index 0000000..72a188d
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsScalarTest.php
@@ -0,0 +1,39 @@
+assertDescription('a scalar', scalarValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', scalarValue(), null);
+ $this->assertMismatchDescription('was an array ["foo"]', scalarValue(), array('foo'));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsStringTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsStringTest.php
new file mode 100644
index 0000000..557d591
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Type/IsStringTest.php
@@ -0,0 +1,35 @@
+assertDescription('a string', stringValue());
+ }
+
+ public function testDecribesActualTypeInMismatchMessage()
+ {
+ $this->assertMismatchDescription('was null', stringValue(), null);
+ $this->assertMismatchDescription('was a double <5.2F>', stringValue(), 5.2);
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/UtilTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/UtilTest.php
new file mode 100644
index 0000000..7248978
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/UtilTest.php
@@ -0,0 +1,82 @@
+assertSame($matcher, $newMatcher);
+ }
+
+ public function testWrapValueWithIsEqualWrapsPrimitive()
+ {
+ $matcher = \Hamcrest\Util::wrapValueWithIsEqual('foo');
+ $this->assertInstanceOf('Hamcrest\Core\IsEqual', $matcher);
+ $this->assertTrue($matcher->matches('foo'));
+ }
+
+ public function testCheckAllAreMatchersAcceptsMatchers()
+ {
+ \Hamcrest\Util::checkAllAreMatchers(array(
+ new \Hamcrest\Text\MatchesPattern('/fo+/'),
+ new \Hamcrest\Core\IsEqual('foo'),
+ ));
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ */
+ public function testCheckAllAreMatchersFailsForPrimitive()
+ {
+ \Hamcrest\Util::checkAllAreMatchers(array(
+ new \Hamcrest\Text\MatchesPattern('/fo+/'),
+ 'foo',
+ ));
+ }
+
+ private function callAndAssertCreateMatcherArray($items)
+ {
+ $matchers = \Hamcrest\Util::createMatcherArray($items);
+ $this->assertInternalType('array', $matchers);
+ $this->assertSameSize($items, $matchers);
+ foreach ($matchers as $matcher) {
+ $this->assertInstanceOf('\Hamcrest\Matcher', $matcher);
+ }
+
+ return $matchers;
+ }
+
+ public function testCreateMatcherArrayLeavesMatchersUntouched()
+ {
+ $matcher = new \Hamcrest\Text\MatchesPattern('/fo+/');
+ $items = array($matcher);
+ $matchers = $this->callAndAssertCreateMatcherArray($items);
+ $this->assertSame($matcher, $matchers[0]);
+ }
+
+ public function testCreateMatcherArrayWrapsPrimitiveWithIsEqualMatcher()
+ {
+ $matchers = $this->callAndAssertCreateMatcherArray(array('foo'));
+ $this->assertInstanceOf('Hamcrest\Core\IsEqual', $matchers[0]);
+ $this->assertTrue($matchers[0]->matches('foo'));
+ }
+
+ public function testCreateMatcherArrayDoesntModifyOriginalArray()
+ {
+ $items = array('foo');
+ $this->callAndAssertCreateMatcherArray($items);
+ $this->assertSame('foo', $items[0]);
+ }
+
+ public function testCreateMatcherArrayUnwrapsSingleArrayElement()
+ {
+ $matchers = $this->callAndAssertCreateMatcherArray(array(array('foo')));
+ $this->assertInstanceOf('Hamcrest\Core\IsEqual', $matchers[0]);
+ $this->assertTrue($matchers[0]->matches('foo'));
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Xml/HasXPathTest.php b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Xml/HasXPathTest.php
new file mode 100644
index 0000000..6774887
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/Hamcrest/Xml/HasXPathTest.php
@@ -0,0 +1,198 @@
+
+
+
+ alice
+ Alice Frankel
+ admin
+
+
+ bob
+ Bob Frankel
+ user
+
+
+ charlie
+ Charlie Chan
+ user
+
+
+XML;
+ self::$doc = new \DOMDocument();
+ self::$doc->loadXML(self::$xml);
+
+ self::$html = <<
+
+ Home Page
+
+
+ Heading
+ Some text
+
+
+HTML;
+ }
+
+ protected function createMatcher()
+ {
+ return \Hamcrest\Xml\HasXPath::hasXPath('/users/user');
+ }
+
+ public function testMatchesWhenXPathIsFound()
+ {
+ assertThat('one match', self::$doc, hasXPath('user[id = "bob"]'));
+ assertThat('two matches', self::$doc, hasXPath('user[role = "user"]'));
+ }
+
+ public function testDoesNotMatchWhenXPathIsNotFound()
+ {
+ assertThat(
+ 'no match',
+ self::$doc,
+ not(hasXPath('user[contains(id, "frank")]'))
+ );
+ }
+
+ public function testMatchesWhenExpressionWithoutMatcherEvaluatesToTrue()
+ {
+ assertThat(
+ 'one match',
+ self::$doc,
+ hasXPath('count(user[id = "bob"])')
+ );
+ }
+
+ public function testDoesNotMatchWhenExpressionWithoutMatcherEvaluatesToFalse()
+ {
+ assertThat(
+ 'no matches',
+ self::$doc,
+ not(hasXPath('count(user[id = "frank"])'))
+ );
+ }
+
+ public function testMatchesWhenExpressionIsEqual()
+ {
+ assertThat(
+ 'one match',
+ self::$doc,
+ hasXPath('count(user[id = "bob"])', 1)
+ );
+ assertThat(
+ 'two matches',
+ self::$doc,
+ hasXPath('count(user[role = "user"])', 2)
+ );
+ }
+
+ public function testDoesNotMatchWhenExpressionIsNotEqual()
+ {
+ assertThat(
+ 'no match',
+ self::$doc,
+ not(hasXPath('count(user[id = "frank"])', 2))
+ );
+ assertThat(
+ 'one match',
+ self::$doc,
+ not(hasXPath('count(user[role = "admin"])', 2))
+ );
+ }
+
+ public function testMatchesWhenContentMatches()
+ {
+ assertThat(
+ 'one match',
+ self::$doc,
+ hasXPath('user/name', containsString('ice'))
+ );
+ assertThat(
+ 'two matches',
+ self::$doc,
+ hasXPath('user/role', equalTo('user'))
+ );
+ }
+
+ public function testDoesNotMatchWhenContentDoesNotMatch()
+ {
+ assertThat(
+ 'no match',
+ self::$doc,
+ not(hasXPath('user/name', containsString('Bobby')))
+ );
+ assertThat(
+ 'no matches',
+ self::$doc,
+ not(hasXPath('user/role', equalTo('owner')))
+ );
+ }
+
+ public function testProvidesConvenientShortcutForHasXPathEqualTo()
+ {
+ assertThat('matches', self::$doc, hasXPath('count(user)', 3));
+ assertThat('matches', self::$doc, hasXPath('user[2]/id', 'bob'));
+ }
+
+ public function testProvidesConvenientShortcutForHasXPathCountEqualTo()
+ {
+ assertThat('matches', self::$doc, hasXPath('user[id = "charlie"]', 1));
+ }
+
+ public function testMatchesAcceptsXmlString()
+ {
+ assertThat('accepts XML string', self::$xml, hasXPath('user'));
+ }
+
+ public function testMatchesAcceptsHtmlString()
+ {
+ assertThat('accepts HTML string', self::$html, hasXPath('body/h1', 'Heading'));
+ }
+
+ public function testHasAReadableDescription()
+ {
+ $this->assertDescription(
+ 'XML or HTML document with XPath "/users/user"',
+ hasXPath('/users/user')
+ );
+ $this->assertDescription(
+ 'XML or HTML document with XPath "count(/users/user)" <2>',
+ hasXPath('/users/user', 2)
+ );
+ $this->assertDescription(
+ 'XML or HTML document with XPath "/users/user/name"'
+ . ' a string starting with "Alice"',
+ hasXPath('/users/user/name', startsWith('Alice'))
+ );
+ }
+
+ public function testHasAReadableMismatchDescription()
+ {
+ $this->assertMismatchDescription(
+ 'XPath returned no results',
+ hasXPath('/users/name'),
+ self::$doc
+ );
+ $this->assertMismatchDescription(
+ 'XPath expression result was <3F>',
+ hasXPath('/users/user', 2),
+ self::$doc
+ );
+ $this->assertMismatchDescription(
+ 'XPath returned ["alice", "bob", "charlie"]',
+ hasXPath('/users/user/id', 'Frank'),
+ self::$doc
+ );
+ }
+}
diff --git a/vendor/hamcrest/hamcrest-php/tests/bootstrap.php b/vendor/hamcrest/hamcrest-php/tests/bootstrap.php
new file mode 100644
index 0000000..bc4958d
--- /dev/null
+++ b/vendor/hamcrest/hamcrest-php/tests/bootstrap.php
@@ -0,0 +1,11 @@
+
+
+
+ .
+
+
+
+
+
+ ../hamcrest
+
+
+
diff --git a/vendor/laravel/framework/CHANGELOG.md b/vendor/laravel/framework/CHANGELOG.md
new file mode 100644
index 0000000..7e93818
--- /dev/null
+++ b/vendor/laravel/framework/CHANGELOG.md
@@ -0,0 +1,643 @@
+# Release Notes for 9.x
+
+## [Unreleased](https://github.com/laravel/framework/compare/v9.19.0...9.x)
+
+
+## [v9.19.0](https://github.com/laravel/framework/compare/v9.18.0...v9.19.0) - 2022-06-28
+
+### Added
+- Add new allowMaxRedirects method to PendingRequest ([#42902](https://github.com/laravel/framework/pull/42902))
+- Add support to detect dirty encrypted model attributes ([#42888](https://github.com/laravel/framework/pull/42888))
+- Added Vite ([#42785](https://github.com/laravel/framework/pull/42785))
+
+### Fixed
+- Fixed bug on forceCreate on a MorphMay relationship not including morph type ([#42929](https://github.com/laravel/framework/pull/42929))
+- Fix default parameter bug in routes ([#42942](https://github.com/laravel/framework/pull/42942))
+- Handle cursor paginator when no items are found ([#42963](https://github.com/laravel/framework/pull/42963))
+- Fix undefined constant error when use slot name as key of object ([#42943](https://github.com/laravel/framework/pull/42943))
+- Fix BC break for Log feature tests ([#42987](https://github.com/laravel/framework/pull/42987))
+
+### Changed
+- Allow instance of Enum pass Enum Rule ([#42906](https://github.com/laravel/framework/pull/42906))
+
+
+## [v9.18.0](https://github.com/laravel/framework/compare/v9.17.0...v9.18.0) - 2022-06-21
+
+### Added
+- Improve file attachment for mail and notifications ([#42563](https://github.com/laravel/framework/pull/42563))
+- Introduce Invokable validation classes ([#42689](https://github.com/laravel/framework/pull/42689))
+- Predis v2.0 ([#42577](https://github.com/laravel/framework/pull/42577))
+- Added `Illuminate/View/Compilers/Concerns/CompilesConditionals::compileReadonly()` ([#42717](https://github.com/laravel/framework/pull/42717))
+- Apply where's from union query builder in cursor pagination ([#42651](https://github.com/laravel/framework/pull/42651))
+- Added ability to define "with" relations as a nested array ([#42690](https://github.com/laravel/framework/pull/42690))
+- Added ability to set backoff in broadcast events ([#42737](https://github.com/laravel/framework/pull/42737))
+- Added host(), httpHost(), schemeAndHttpHost() to Request ([#42797](https://github.com/laravel/framework/pull/42797))
+- Allow invokable rules to push messages to nested (or other) attributes ([#42801](https://github.com/laravel/framework/pull/42801))
+- Adds compilePushIf and compileEndpushIf functions to View compiler ([#42762](https://github.com/laravel/framework/pull/42762))
+- Added: Allow removing token during tests ([#42841](https://github.com/laravel/framework/pull/42841))
+- Added predefined_constants to reservedNames array in `Illuminate/Console/GeneratorCommand` ([#42832](https://github.com/laravel/framework/pull/42832))
+- Handle collection creation around a single enum ([#42839](https://github.com/laravel/framework/pull/42839))
+- Allow for nullable morphs in whereNotMorphedT ([#42878](https://github.com/laravel/framework/pull/42878))
+- Introduce a fake() helper to resolve faker singletons, per locale ([#42844](https://github.com/laravel/framework/pull/42844))
+- Allow handling cumulative query duration limit per DB connection ([#42744](https://github.com/laravel/framework/pull/42744))
+- Add invokable option to make rule command ([#42742](https://github.com/laravel/framework/pull/42742))
+
+### Fixed
+- Fix deprecation error in the route:list command ([#42704](https://github.com/laravel/framework/pull/42704))
+- Fixed Request offsetExists without routeResolver ([#42754](https://github.com/laravel/framework/pull/42754))
+- Fixed: Loose comparison causes the value not to be saved ([#42793](https://github.com/laravel/framework/pull/42793))
+- Fixed: Fix database session driver keeps resetting CSRF token ([#42782](https://github.com/laravel/framework/pull/42782))
+- Fixed: Arr::map - Fix map-by-reference w/ built-ins ([#42815](https://github.com/laravel/framework/pull/42815))
+- Fixed league/flysystem suggest ([#42872](https://github.com/laravel/framework/pull/42872))
+
+### Changed
+- Start session in TestResponse to allow marshalling of error bag from JSON ([#42710](https://github.com/laravel/framework/pull/42710))
+- Rename methods in `Illuminate/Broadcasting/BroadcastManager` ([753e9fd](https://github.com/laravel/framework/commit/753e9fd8843c043938e20b038999fe0a26de6e16))
+- Avoid throwing on invalid mime-type in `Illuminate/Filesystem/FilesystemAdapter::mimeType()` ([#42761](https://github.com/laravel/framework/pull/42761))
+- Do not resolve already set headers in `Illuminate/Filesystem/FilesystemAdapter` ([#42760](https://github.com/laravel/framework/pull/42760))
+- Standardise invokable rule translation functionality ([#42873](https://github.com/laravel/framework/pull/42873))
+- Clear cast cache when setting attributes using arrow ([#42852](https://github.com/laravel/framework/pull/42852))
+
+
+## [v9.17.0](https://github.com/laravel/framework/compare/v9.16.0...v9.17.0) - 2022-06-07
+
+### Added
+- Added Illuminate/Database/Eloquent/Builder::withoutEagerLoad() ([#42641](https://github.com/laravel/framework/pull/42641))
+- Allow random string generation to be controlled ([#42669](https://github.com/laravel/framework/pull/42669))
+- Adds doesnt_start_with validation ([#42683](https://github.com/laravel/framework/pull/42683), [de35bf2](https://github.com/laravel/framework/commit/de35bf2a8ab40013d997c62b5a80cdb907c73b99))
+- Added quarterlyOn cron schedule frequency command ([#42692](https://github.com/laravel/framework/pull/42692))
+
+### Fixed
+- Free reserved memory before handling fatal errors ([#42630](https://github.com/laravel/framework/pull/42630), [#42646](https://github.com/laravel/framework/pull/42646))
+- Prevent $mailer being reset when testing mailables that implement ShouldQueue ([#42695](https://github.com/laravel/framework/pull/42695))
+- Added checks for Pusher 7.1 preps ([#42632](https://github.com/laravel/framework/pull/42632))
+- Fixed grouping for user authorization ([#42664](https://github.com/laravel/framework/pull/42664))
+
+### Changed
+- Allow assertions against pushed string based pushed jobs ([#42676](https://github.com/laravel/framework/pull/42676))
+
+
+## [v9.16.0](https://github.com/laravel/framework/compare/v9.15.0...v9.16.0) - 2022-06-02
+
+### Added
+- Added Eloquent withWhereHas method ([#42597](https://github.com/laravel/framework/pull/42597))
+- User authentication for Pusher ([#42531](https://github.com/laravel/framework/pull/42531))
+- Added additional uuid testing helpers ([#42619](https://github.com/laravel/framework/pull/42619))
+
+### Fixed
+- Fix queueable notification's ID overwritten ([#42581](https://github.com/laravel/framework/pull/42581))
+- Handle undefined array key error in route ([#42606](https://github.com/laravel/framework/pull/42606))
+
+### Deprecated
+- Illuminate/Routing/Redirector::home() ([#42600](https://github.com/laravel/framework/pull/42600))
+
+
+## [v9.15.0](https://github.com/laravel/framework/compare/v9.14.1...v9.15.0) - 2022-05-31
+
+### Added
+- Added --only-vendor option to route:list command ([#42549](https://github.com/laravel/framework/pull/42549))
+- Added `Illuminate/Http/Client/PendingRequest::throwUnless()` ([#42556](https://github.com/laravel/framework/pull/42556))
+- Added `Illuminate/Support/Str::isJson()` ([#42545](https://github.com/laravel/framework/pull/42545))
+- Added `Illuminate/Filesystem/Filesystem::isEmptyDirectory()` ([#42559](https://github.com/laravel/framework/pull/42559))
+- Added `Add counts to route:list command` ([#42551](https://github.com/laravel/framework/pull/42551))
+- Support kebab case for slot name shortcut ([#42574](https://github.com/laravel/framework/pull/42574))
+
+### Revered
+- Revert digits changes in validation ([c113768](https://github.com/laravel/framework/commit/c113768dbd47de7466d703108eaf8155916d5666), [#42562](https://github.com/laravel/framework/pull/42562))
+
+### Fixed
+- Fix getting '0' from route parameter in Authorize middleware ([#42582](https://github.com/laravel/framework/pull/42582))
+
+### Changed
+- Retain the original attribute value during validation of an array key with a dot for correct failure message ([#42395](https://github.com/laravel/framework/pull/42395))
+- Allow bootable test traits to teardown ([#42521](https://github.com/laravel/framework/pull/42521))
+- Pass thrown exception to $sleepMilliseconds closure in retry helper ([#42532](https://github.com/laravel/framework/pull/42532))
+- Make HasTimestamps::updateTimestamps chainable ([#42533](https://github.com/laravel/framework/pull/42533))
+- Remove meaningless parameter in `Illuminate/View/Concerns/ManagesEvents` ([#42546](https://github.com/laravel/framework/pull/42546))
+- Map integer parameter to parameter name when resolving binding field ([#42571](https://github.com/laravel/framework/pull/42571))
+- Conditionable should return HigherOrderWhenProxy only when the args number is exactly 1 ([#42555](https://github.com/laravel/framework/pull/42555))
+
+
+## [v9.14.1](https://github.com/laravel/framework/compare/v9.14.0...v9.14.1) - 2022-05-25
+
+### Added
+- Model::whereRelation add callback function ([#42491](https://github.com/laravel/framework/pull/42491))
+- Add Conditionable Trait to Illuminate\Support\Carbon ([#42500](https://github.com/laravel/framework/pull/42500))
+
+### Fixed
+- Fix afterCommit and DatabaseTransactions ([#42502](https://github.com/laravel/framework/pull/42502))
+- Fixed regression when only some route parameters are scoped ([#42517](https://github.com/laravel/framework/pull/42517))
+
+
+## [v9.14.0](https://github.com/laravel/framework/compare/v9.13.0...v9.14.0) - 2022-05-24
+
+### Added
+- Added ability to add table comments for MySQL and Postgres ([#42401](https://github.com/laravel/framework/pull/42401))
+- Added dynamic trashed factory state ([#42414](https://github.com/laravel/framework/pull/42414))
+- Added Illuminate/Collections/Arr::prependKeysWith() ([#42448](https://github.com/laravel/framework/pull/42448))
+- Added bootable traits to TestCase ([#42394](https://github.com/laravel/framework/pull/42394))
+
+### Fixed
+- Fix clone issue on updateOrCreate and firstOrCreate ([#42434](https://github.com/laravel/framework/pull/42434))
+- Prevent double sanitized key in RateLimiter@tooManyAttempts ([#42462](https://github.com/laravel/framework/pull/42462))
+- Add flush handler to output buffer for streamed test response (bugfix) ([#42481](https://github.com/laravel/framework/pull/42481))
+
+### Changed
+- Adds attaches a concise error message to SES exceptions ([#42426](https://github.com/laravel/framework/pull/42426))
+- Use duplicate instead of createFromBase to clone request when routes are cached ([#42420](https://github.com/laravel/framework/pull/42420))
+- Use model route key when route parameter does not specifiy custom binding field but a different parameter does ([#42425](https://github.com/laravel/framework/pull/42425))
+- Adds ability to have paginate() $perPage parameter as callable with access to $total ([#42429](https://github.com/laravel/framework/pull/42429))
+- Extract ServeCommand env list to static property ([#42444](https://github.com/laravel/framework/pull/42444))
+- Use route parameters in view ([#42461](https://github.com/laravel/framework/pull/42461))
+
+
+## [v9.13.0](https://github.com/laravel/framework/compare/v9.12.2...v9.13.0) - 2022-05-17
+
+### Added
+- Added Illuminate/Collections/Traits/EnumeratesValues::value() ([#42257](https://github.com/laravel/framework/pull/42257))
+- Added new TestResponse helper: assertJsonMissingPath ([#42361](https://github.com/laravel/framework/pull/42361))
+- Added Illuminate/Support/Testing/Fakes/NotificationFake::assertCount() ([#42366](https://github.com/laravel/framework/pull/42366))
+- Added new DetectLostConnections ([#42377](https://github.com/laravel/framework/pull/42377), [#42382](https://github.com/laravel/framework/pull/42382))
+- Added Illuminate/Testing/TestResponse::collect() ([#42384](https://github.com/laravel/framework/pull/42384))
+- Added full callable support to schedule:list ([#42400](https://github.com/laravel/framework/pull/42400))
+- Added `Illuminate/Collections/Arr::map()` ([#42398](https://github.com/laravel/framework/pull/42398))
+
+### Fixed
+- Fixed PruneCommand finding its usage within other traits ([#42350](https://github.com/laravel/framework/pull/42350))
+- Fix assert that exception is thrown without message ([#42360](https://github.com/laravel/framework/pull/42360))
+
+### Changed
+- Skip parameter parsing for raw post body in HTTP Client ([#42364](https://github.com/laravel/framework/pull/42364))
+- Consistency between digits and digits_between validation rules ([#42358](https://github.com/laravel/framework/pull/42358))
+- Corrects the use of "failed_jobs" instead of "job_batches" in BatchedTableCommand ([#42389](https://github.com/laravel/framework/pull/42389))
+- Update email.blade.php ([#42388](https://github.com/laravel/framework/pull/42388))
+- Remove old monolog 1.x compat code ([#42392](https://github.com/laravel/framework/pull/42392))
+- SesTransport: use correct Tags argument ([#42390](https://github.com/laravel/framework/pull/42390))
+- Implement robust handling of forwarding of exception codes ([#42393](https://github.com/laravel/framework/pull/42393))
+
+
+## [v9.12.2](https://github.com/laravel/framework/compare/v9.12.1...v9.12.2) - 2022-05-11
+
+### Fixed
+- Factory fails to eval models and factories when returned from closure ([#42344](https://github.com/laravel/framework/pull/42344))
+
+### Changed
+- Added is_string check to QueriesRelationships@requalifyWhereTables ([#42341](https://github.com/laravel/framework/pull/42341))
+
+
+## [v9.12.1](https://github.com/laravel/framework/compare/v9.12.0...v9.12.1) - 2022-05-10
+
+### Fixed
+- Fix TypeError in DeadlockException ([#42337](https://github.com/laravel/framework/pull/42337))
+- Fixed type mismatch on Pusher::validate_channels() ([#42340](https://github.com/laravel/framework/pull/42340))
+
+### Changed
+- Add custom segments on "remember me" for session rebuild ([#42316](https://github.com/laravel/framework/pull/42316))
+
+
+## [v9.12.0](https://github.com/laravel/framework/compare/v9.11.0...v9.12.0) - 2022-05-10
+
+### Added
+
+- Added closure based exceptions testing ([#42155](https://github.com/laravel/framework/pull/42155))
+- Allow forcing requests made via the Http client to be faked ([#42230](https://github.com/laravel/framework/pull/42230))
+- Added 'throwIf' method to PendingRequest ([#42260](https://github.com/laravel/framework/pull/42260))
+- Allow passing key/value arrays to getArguments and getOptions ([#42268](https://github.com/laravel/framework/pull/42268))
+- Add whereNotMorphedTo, orWhereNotMorphedTo ([#42264](https://github.com/laravel/framework/pull/42264))
+- Add method to extend localeArray generation ([#42275](https://github.com/laravel/framework/pull/42275))
+- Added ability to set delay per channel based on notifiable instance ([#42239](https://github.com/laravel/framework/pull/42239))
+- Added Illuminate/Pagination/CursorPaginator::onLastPage() ([#42301](https://github.com/laravel/framework/pull/42301))
+- Added findOr method to Query/Builder ([#42290](https://github.com/laravel/framework/pull/42290))
+
+### Fixed
+
+- Fix too many channels with pusher broadcasting ([#42287](https://github.com/laravel/framework/pull/42287))
+- Fix Str::Mask() for repeating chars ([#42295](https://github.com/laravel/framework/pull/42295))
+- Fix EventFake::assertListening() for asserting string-based observer listeners ([#42289](https://github.com/laravel/framework/pull/42289))
+- Fixed Loose comparison causes the value not to be saved ([#41337](https://github.com/laravel/framework/pull/41337))
+- Fix multiple dots for digits_between rule ([#42330](https://github.com/laravel/framework/pull/42330))
+
+### Changed
+
+- Enable to modify HTTP Client request headers when using beforeSending() callback ([#42244](https://github.com/laravel/framework/pull/42244))
+- Make throttle lock acquisition retry configurable for concurrency limiter ([#42242](https://github.com/laravel/framework/pull/42242))
+- Defers expanding callables on Factories ([#42241](https://github.com/laravel/framework/pull/42241))
+- Add wherehas soft deleting scopes ([#42100](https://github.com/laravel/framework/pull/42100))
+- Improve password checks ([#42248](https://github.com/laravel/framework/pull/42248))
+- Set relation parent key when using forceCreate on HasOne and HasMany relations ([#42281](https://github.com/laravel/framework/pull/42281))
+- Make sure the prefix override behaviours are the same between phpredis and predis drivers ([#42279](https://github.com/laravel/framework/pull/42279))
+- Share logging context across channels and stacks ([#42276](https://github.com/laravel/framework/pull/42276))
+
+## [v9.11.0](https://github.com/laravel/framework/compare/v9.10.1...v9.11.0) - 2022-05-03
+
+### Added
+
+- Added Illuminate/Collections/Arr::join() ([#42197](https://github.com/laravel/framework/pull/42197))
+- Added has and missing methods to ValidatedInput ([#42184](https://github.com/laravel/framework/pull/42184))
+- Added deprecation stack trace config option ([#42235](https://github.com/laravel/framework/pull/42235))
+
+### Fixed
+
+- Fix deprecation issue with translator and empty rules ([#42216](https://github.com/laravel/framework/pull/42216), [#42213](https://github.com/laravel/framework/pull/42213))
+
+### Changed
+
+- Improve EventFake::assertListening() support for subscribers ([#42193](https://github.com/laravel/framework/pull/42193))
+
+## [v9.10.1](https://github.com/laravel/framework/compare/v9.10.0...v9.10.1) - 2022-04-28
+
+### Revert
+
+- Revert of ["Illuminate/Routing/Router::middlewareGroup() will support array of the middlewares"](https://github.com/laravel/framework/pull/42004) ([7563912](https://github.com/laravel/framework/commit/75639121cc55d4390fd75a0f422c7f0a626ece1e))
+
+## [v9.10.0](https://github.com/laravel/framework/compare/v9.9.0...v9.10.0) - 2022-04-27
+
+### Added
+
+- Add the ability to use alias when performing upsert via MySQL ([#42053](https://github.com/laravel/framework/pull/42053))
+- Illuminate/Routing/Router::middlewareGroup() will support array of the middlewares ([#42004](https://github.com/laravel/framework/pull/42004), [e6b84fb](https://github.com/laravel/framework/commit/e6b84fb0f1f1c82ce1a486643e2b20974522cda6))
+- Added Missing AsCommand attribute on schedule:list ([#42069](https://github.com/laravel/framework/pull/42069))
+- Add the beforeRefreshingDatabase function to the Testing/RefreshDatabase trait ([#42073](https://github.com/laravel/framework/pull/42073))
+- Added doesntExpectOutputToContain assertion method ([#42096](https://github.com/laravel/framework/pull/42096))
+- Added a findOr method to Eloquent ([#42092](https://github.com/laravel/framework/pull/42092))
+- Allow extension in `Illuminate/View/Compilers/Compiler.php` ([68e41fd](https://github.com/laravel/framework/commit/68e41fd3691b9aa5548e07c5caf38696c4082513))
+- Support 'IS' and 'IS NOT' PostgreSQL operators ([#42123](https://github.com/laravel/framework/pull/42123))
+- Added `str` and `string` methods to Illuminate/Http/Concerns/InteractsWithInput ([c9d34b7](https://github.com/laravel/framework/commit/c9d34b7be0611d26f3e46669934cf542cc5e9e21))
+- Added methods to append and prepend jobs to existing chain ([#42138](https://github.com/laravel/framework/pull/42138))
+
+### Fixes
+
+- Make it so non-existent jobs run down the failed path instead of crashing ([#42079](https://github.com/laravel/framework/pull/42079))
+- Fix schedule:work command Artisan binary name ([#42083](https://github.com/laravel/framework/pull/42083))
+- Fix TrimStrings middleware with non-UTF8 characters ([#42065](https://github.com/laravel/framework/pull/42065))
+- Copy locale and defaultLocale from original request in Request::createFrom() ([#42080](https://github.com/laravel/framework/pull/42080))
+- Fix ViewErrorBag for JSON session serialization ([#42090](https://github.com/laravel/framework/pull/42090))
+- Fix array keys from cached routes in CompiledRouteCollection::getRoutesByMethod() ([#42078](https://github.com/laravel/framework/pull/42078))
+- Fix json_last_error issue with JsonResponse::setData ([#42125](https://github.com/laravel/framework/pull/42125))
+- Fix bug in BelongsToMany where non-related rows are returned ([#42087](https://github.com/laravel/framework/pull/42087))
+- Fix HasAttributes::mutateAttributeForArray when accessing non-cached attribute ([#42130](https://github.com/laravel/framework/pull/42130))
+
+### Changed
+
+- Make password rule errors translatable ([#42060](https://github.com/laravel/framework/pull/42060))
+- Redesign of the event:list Command. ([#42068](https://github.com/laravel/framework/pull/42068))
+- Changed event:list command ([#42084](https://github.com/laravel/framework/pull/42084))
+- Throw LostDbConnectionException instead of LogicException ([#42102](https://github.com/laravel/framework/pull/42102))
+- Throw deadlock exception ([#42129](https://github.com/laravel/framework/pull/42129))
+- Support Arr::forget() for nested ArrayAccess objects ([#42142](https://github.com/laravel/framework/pull/42142))
+- Allow Illuminate/Collections/Enumerable::jsonSerialize() to return other types ([#42133](https://github.com/laravel/framework/pull/42133))
+- Update schedule:list colouring output ([#42153](%5B#42153%5D(https://github.com/laravel/framework/pull/42153))
+
+## [v9.9.0](https://github.com/laravel/framework/compare/v9.8.1...v9.9.0) - 2022-04-19
+
+### Added
+
+- Add getAllTables support for SQLite and SQLServer schema builders ([#41896](https://github.com/laravel/framework/pull/41896))
+- Added withoutEagerLoads() method to Builder ([#41950](https://github.com/laravel/framework/pull/41950))
+- Added 'throw' method to PendingRequest ([#41953](https://github.com/laravel/framework/pull/41953))
+- Configurable pluralizer language and uncountables ([#41941](https://github.com/laravel/framework/pull/41941))
+
+### Fixed
+
+- Fixed Error in Illuminate/Routing/Exceptions/StreamedResponseException ([#41955](https://github.com/laravel/framework/pull/41955))
+- Fix PHP warnings when rendering long blade string ([#41956](https://github.com/laravel/framework/pull/41956))
+- Fix ExcludeIf regression to use Closure over is_callable() ([#41969](https://github.com/laravel/framework/pull/41969))
+- Fixes applying replacements to multi-level localization arrays ([#42022](https://github.com/laravel/framework/pull/42022))
+
+### Changed
+
+- Improved Illuminate/Foundation/Http/Middleware/TrimStrings.php and Str::squish() ([#41949](https://github.com/laravel/framework/pull/41949), [#41971](https://github.com/laravel/framework/pull/41971))
+- Use config session domain for maintenance cookie ([#41961](https://github.com/laravel/framework/pull/41961))
+- Revert lazy command forcing ([#41982](https://github.com/laravel/framework/pull/41982))
+
+## [v9.8.1](https://github.com/laravel/framework/compare/v9.8.0...v9.8.1) - 2022-04-12
+
+### Reverted
+
+- Revert "Standardize withCount() & withExists() eager loading aggregates ([#41943](https://github.com/laravel/framework/pull/41943))
+
+## [v9.8.0](https://github.com/laravel/framework/compare/v9.7.0...v9.8.0) - 2022-04-12
+
+### Added
+
+- Added inbound option to CastMakeCommand ([#41838](https://github.com/laravel/framework/pull/41838))
+- Added a way to retrieve the first column of the first row from a query ([#41858](https://github.com/laravel/framework/pull/41858))
+- Make DatabaseManager Macroable ([#41868](https://github.com/laravel/framework/pull/41868))
+- Improve Str::squish() ([#41877](https://github.com/laravel/framework/pull/41877), [#41924](https://github.com/laravel/framework/pull/41924))
+- Added option to disable cached view ([#41859](https://github.com/laravel/framework/pull/41859))
+- Make Connection Class Macroable ([#41865](https://github.com/laravel/framework/pull/41865))
+- Added possibility to discover anonymous Blade components in other folders ([#41637](https://github.com/laravel/framework/pull/41637))
+- Added `Illuminate/Database/Eloquent/Factories/Factory::set()` ([#41890](https://github.com/laravel/framework/pull/41890))
+- Added multibyte support to string padding helper functions ([#41899](https://github.com/laravel/framework/pull/41899))
+- Allow to use custom log level in exception handler reporting ([#41925](https://github.com/laravel/framework/pull/41925))
+
+### Fixed
+
+- Illuminate/Support/Stringable::exactly() with Stringable value ([#41846](https://github.com/laravel/framework/pull/41846))
+- Fixed afterCommit and RefreshDatabase ([#41782](https://github.com/laravel/framework/pull/41782))
+- Fix null name for email address in `Illuminate/Mail/Message` ([#41870](https://github.com/laravel/framework/pull/41870))
+- Fix seeder property for in-memory tests ([#41869](https://github.com/laravel/framework/pull/41869))
+- Fix empty paths for server.php ([#41933](https://github.com/laravel/framework/pull/41933))
+- Fix ExcludeIf constructor ([#41931](https://github.com/laravel/framework/pull/41931))
+
+### Changed
+
+- Set custom host to the serve command with environment variable ([#41831](https://github.com/laravel/framework/pull/41831))
+- Add handling of object being passed into old method in Model ([#41842](https://github.com/laravel/framework/pull/41842))
+- Catch permission exception when creating directory ([#41871](https://github.com/laravel/framework/pull/41871))
+- Restore v8 behaviour of base query for relations ([#41918](https://github.com/laravel/framework/pull/41918), [#41923](https://github.com/laravel/framework/pull/41923))
+- Standardize withCount() & withExists() eager loading aggregates ([#41914](https://github.com/laravel/framework/pull/41914))
+
+## [v9.7.0](https://github.com/laravel/framework/compare/v9.6.0...v9.7.0) - 2022-04-05
+
+### Added
+
+- Make whereBelongsTo accept Collection ([#41733](https://github.com/laravel/framework/pull/41733))
+- Database queries containing JSON paths support array index braces ([#41767](https://github.com/laravel/framework/pull/41767))
+- Fire event before route matched ([#41765](https://github.com/laravel/framework/pull/41765))
+- Added to `Illuminate/Http/Resources/ConditionallyLoadsAttributes::whenNotNull` method ([#41769](https://github.com/laravel/framework/pull/41769))
+- Added "whereIn" route parameter constraint method ([#41794](https://github.com/laravel/framework/pull/41794))
+- Added `Illuminate/Queue/BeanstalkdQueue::bulk()` ([#41789](https://github.com/laravel/framework/pull/41789))
+- Added `Illuminate/Queue/SqsQueue::bulk()` ([#41788](https://github.com/laravel/framework/pull/41788))
+- Added String::squish() helper ([#41791](https://github.com/laravel/framework/pull/41791))
+- Added query builder method whereJsonContainsKey() ([#41802](https://github.com/laravel/framework/pull/41802))
+- Enable dispatchAfterResponse for batch ([#41787](https://github.com/laravel/framework/pull/41787))
+
+### Fixed
+
+- Factory generation fixes ([#41688](https://github.com/laravel/framework/pull/41688))
+- Fixed Http Client throw boolean parameter of retry method ([#41762](https://github.com/laravel/framework/pull/41762), [#41792](https://github.com/laravel/framework/pull/41792))
+- Ignore empty redis username string in PhpRedisConnector ([#41773](https://github.com/laravel/framework/pull/41773))
+- Fixed support of nullable type for AsArrayObject/AsCollection ([#41797](https://github.com/laravel/framework/pull/41797), [05846e7](https://github.com/laravel/framework/commit/05846e7ba5cecc12a3ab8a3238272e9c1dd4e07f))
+- Fixed adding jobs from iterable to the pending batch ([#41786](https://github.com/laravel/framework/pull/41786))
+- Http client: fix retry handling of connection exception ([#41811](https://github.com/laravel/framework/pull/41811))
+
+### Changed
+
+- Enable batch jobs delay for database queue ([#41758](https://github.com/laravel/framework/pull/41758))
+- Enable batch jobs delay for redis queue ([#41783](https://github.com/laravel/framework/pull/41783))
+- Http client: dispatch "response received" event for every retry attempt ([#41793](https://github.com/laravel/framework/pull/41793))
+- Http Client: provide pending request to retry callback ([#41779](https://github.com/laravel/framework/pull/41779))
+- Allow non length limited strings and char for postgresql ([#41800](https://github.com/laravel/framework/pull/41800))
+- Revert some Carbon::setTestNow() removals ([#41810](https://github.com/laravel/framework/pull/41810))
+- Allow cleanup of databases when using parallel tests ([#41806](https://github.com/laravel/framework/pull/41806))
+
+## [v9.6.0](https://github.com/laravel/framework/compare/v9.5.1...v9.6.0) - 2022-03-29
+
+### Added
+
+- Added whenTableHasColumn and whenTableDoesntHaveColumn on Schema Builder ([#41517](https://github.com/laravel/framework/pull/41517))
+- Added Illuminate/Mail/Mailable::hasSubject() ([#41575](https://github.com/laravel/framework/pull/41575))
+- Added `Illuminate/Filesystem/Filesystem::hasSameHash()` ([#41586](https://github.com/laravel/framework/pull/41586))
+
+### Fixed
+
+- Fixed deprecation warning in `Str::exists()` ([d39d92d](https://github.com/laravel/framework/commit/d39d92df9b3c509d40b971207f03eb7f04087370))
+- Fix artisan make:seeder command nested namespace and class name problem ([#41534](https://github.com/laravel/framework/pull/41534))
+- Fixed Illuminate/Redis/Connections/PhpRedisConnection::handle() ([#41546](https://github.com/laravel/framework/pull/41546))
+- Stop throwing LazyLoadingViolationException for recently created model instances ([#41549](https://github.com/laravel/framework/pull/41549))
+- Close doctrineConnection on disconnect ([#41584](https://github.com/laravel/framework/pull/41584))
+
+### Changed
+
+- Make throttle lock acquisition retry time configurable ([#41516](https://github.com/laravel/framework/pull/41516))
+- Allows object instead of array when adding to PendingBatch ([#41475](https://github.com/laravel/framework/pull/41475))
+- Exactly match scheduled command --name in schedule:test ([#41528](https://github.com/laravel/framework/pull/41528))
+- Handle Symfony defaultName deprecation ([#41555](https://github.com/laravel/framework/pull/41555), [#41595](https://github.com/laravel/framework/pull/41595))
+- Improve ScheduleListCommand ([#41552](https://github.com/laravel/framework/pull/41552), [#41535](https://github.com/laravel/framework/pull/41535), [#41494](https://github.com/laravel/framework/pull/41494))
+- Remove useless if statement in Str::mask() ([#41570](https://github.com/laravel/framework/pull/41570))
+
+## [v9.5.1](https://github.com/laravel/framework/compare/v9.5.0...v9.5.1) - 2022-03-15
+
+### Reverted
+
+- Revert "Fix the guard instance used." ([#41491](https://github.com/laravel/framework/pull/41491))
+
+## [v9.5.0](https://github.com/laravel/framework/compare/v9.4.1...v9.5.0) - 2022-03-15
+
+### Added
+
+- Added callback support on implode Collection method. ([#41405](https://github.com/laravel/framework/pull/41405))
+- Added `Illuminate/Filesystem/FilesystemAdapter::assertDirectoryEmpty()` ([#41398](https://github.com/laravel/framework/pull/41398))
+- Implement email "metadata" for SesTransport ([#41422](https://github.com/laravel/framework/pull/41422))
+- Make assertPath() accepts Closure ([#41409](https://github.com/laravel/framework/pull/41409))
+- Added callable support to operatorForWhere on Collection ([#41414](https://github.com/laravel/framework/pull/41414), [#41424](https://github.com/laravel/framework/pull/41424))
+- Added partial queue faking ([#41425](https://github.com/laravel/framework/pull/41425))
+- Added --name option to schedule:test command ([#41439](https://github.com/laravel/framework/pull/41439))
+- Define `Illuminate/Database/Eloquent/Concerns/HasRelationships::newRelatedThroughInstance()` ([#41444](https://github.com/laravel/framework/pull/41444))
+- Added `Illuminate/Support/Stringable::wrap()` ([#41455](https://github.com/laravel/framework/pull/41455))
+- Adds "freezeTime" helper for tests ([#41460](https://github.com/laravel/framework/pull/41460))
+- Allow for callables with beforeSending in`Illuminate/Http/Client/PendingRequest.php::runBeforeSendingCallbacks()` ([#41489](https://github.com/laravel/framework/pull/41489))
+
+### Fixed
+
+- Fixed deprecation warnings from route:list when filtering on name or domain ([#41421](https://github.com/laravel/framework/pull/41421))
+- Fixes HTTP::pool response when a URL returns a null status code ([#41412](https://github.com/laravel/framework/pull/41412))
+- Fixed recaller name resolution in `Illuminate/Session/Middleware/AuthenticateSession.php` ([#41429](https://github.com/laravel/framework/pull/41429))
+- Fixed the guard instance used in /Illuminate/Session/Middleware/AuthenticateSession.php ([#41447](https://github.com/laravel/framework/pull/41447))
+- Fixed route:list --except-vendor hiding Route::view() & Route::redirect() ([#41465](https://github.com/laravel/framework/pull/41465))
+
+### Changed
+
+- Add null typing to connection property in \Illuminate\Database\Eloquent\Factories\Factory ([#41418](https://github.com/laravel/framework/pull/41418))
+- Update reserved names in GeneratorCommand ([#41441](https://github.com/laravel/framework/pull/41441))
+- Redesign php artisan schedule:list Command ([#41445](https://github.com/laravel/framework/pull/41445))
+- Extend eloquent higher order proxy properties ([#41449](https://github.com/laravel/framework/pull/41449))
+- Allow passing named arguments to dynamic scopes ([#41478](https://github.com/laravel/framework/pull/41478))
+- Throw if tag is passed but is not supported in `Illuminate/Encryption/Encrypter.php` ([#41479](https://github.com/laravel/framework/pull/41479))
+- Update PackageManifest::$vendorPath initialisation for cases, when composer vendor dir is not in project director ([#41463](https://github.com/laravel/framework/pull/41463))
+
+## [v9.4.1](https://github.com/laravel/framework/compare/v9.4.0...v9.4.1) - 2022-03-08
+
+### Fixed
+
+- Fixed version of laravel
+
+## [v9.4.0](https://github.com/laravel/framework/compare/v9.4.0...v9.4.0) - 2022-03-08
+
+### Added
+
+- Support modifying a char column type ([#41320](https://github.com/laravel/framework/pull/41320))
+- Add "Mutex" column to 'schedule:list' command ([#41338](https://github.com/laravel/framework/pull/41338))
+- Allow eloquent whereNot() and orWhereNot() to work on column and value ([#41296](https://github.com/laravel/framework/pull/41296))
+- Allow VerifyCsrfToken's CSRF cookie to be extended ([#41342](https://github.com/laravel/framework/pull/41342))
+- Added `soleValue()` to query builder ([#41368](https://github.com/laravel/framework/pull/41368))
+- Added `lcfirst()` to `Str` and `Stringable` ([#41384](https://github.com/laravel/framework/pull/41384))
+- Added retryUntil method to queued mailables ([#41393](https://github.com/laravel/framework/pull/41393))
+
+### Fixed
+
+- Fixed middleware sorting for authenticating sessions ([50b46db](https://github.com/laravel/framework/commit/50b46db563e11ba52a53e3046c23e92878aed395))
+- Fixed takeUntilTimeout method of LazyCollection ([#41354](https://github.com/laravel/framework/pull/41354), [#41370](https://github.com/laravel/framework/pull/41370))
+- Fixed directory for nested markdown files for mailables ([#41366](https://github.com/laravel/framework/pull/41366))
+- Prevent serializing default values of queued jobs ([#41348](https://github.com/laravel/framework/pull/41348))
+- Fixed get() and head() in `Illuminate/Http/Client/PendingRequest.php` ([a54f481](https://github.com/laravel/framework/commit/a54f48102deea2864071e510172fe0b22a1c1d5a))
+
+### Changed
+
+- Don't use global tap helper ([#41326](https://github.com/laravel/framework/pull/41326))
+- Allow chaining of `Illuminate/Console/Concerns/InteractsWithIO::newLine` ([#41327](https://github.com/laravel/framework/pull/41327))
+- set destinations since bcc missing from raw message in Mail SesTransport ([8ca43f4](https://github.com/laravel/framework/commit/8ca43f4c2a531ff9d28b86a7e366eef8adf8de84))
+
+## [v9.3.1](https://github.com/laravel/framework/compare/v9.3.0...v9.3.1) - 2022-03-03
+
+### Added
+
+- Optionally cascade thrown Flysystem exceptions by @driesvints in https://github.com/laravel/framework/pull/41308
+
+### Changed
+
+- Allow overriding transport type on Mailgun transporter by @jnoordsij in https://github.com/laravel/framework/pull/41309
+- Change how Laravel handles strict morph map with pivot classes by @crynobone in https://github.com/laravel/framework/pull/41304
+
+### Fixed
+
+- $job can be an object in some methods by @villfa in https://github.com/laravel/framework/pull/41244
+- Fix docblock on Batch class by @yoeriboven in https://github.com/laravel/framework/pull/41295
+- Correct `giveConfig` param doc by @Neol3108 in https://github.com/laravel/framework/pull/41314
+- Fix MySqlSchemaState does not add --ssl-ca to mysql cli by @DeepDiver1975 in https://github.com/laravel/framework/pull/41315
+- Do not prepend baseUrl to absolute urls by @JaZo in https://github.com/laravel/framework/pull/41307
+- Fixes getting the trusted proxies IPs from the configuration file by @nunomaduro in https://github.com/laravel/framework/pull/41322
+
+## [v9.3.0 (2022-03-02)](https://github.com/laravel/framework/compare/v9.2.0...v9.3.0)
+
+### Added
+
+- Add NotificationFake::assertNothingSentTo() by @axlon ([#41232](https://github.com/laravel/framework/pull/41232))
+- Support --ssl-ca on schema load and dump by @DeepDiver1975 ([#40931](https://github.com/laravel/framework/pull/40931))
+- Add whereNot() to Query Builder and Eloquent Builder by @marcovo ([#41096](https://github.com/laravel/framework/pull/41096))
+- Added support for index and position placeholders in array validation messages by @Bird87ZA ([#41123](https://github.com/laravel/framework/pull/41123))
+- Add resource binding by @aedart ([#41233](https://github.com/laravel/framework/pull/41233))
+- Add ability to push additional pipes onto a pipeline via chain($pipes) by @stevebauman ([#41256](https://github.com/laravel/framework/pull/41256))
+- Add option to filter out routes defined in vendor packages in route:list command by @amiranagram ([#41254](https://github.com/laravel/framework/pull/41254))
+
+### Fixed
+
+- Query PostgresBuilder fixes for renamed config 'search_path' by @derekmd ([#41215](https://github.com/laravel/framework/pull/41215))
+- Improve doctypes for Eloquent Factory guessing methods by @bastien-phi ([#41245](https://github.com/laravel/framework/pull/41245))
+- Fix Conditional::when and Conditional::unless when called with invokable by @bastien-phi ([#41270](https://github.com/laravel/framework/pull/41270))
+- Improves Support\Collection reduce method type definition by @fdalcin ([#41272](https://github.com/laravel/framework/pull/41272))
+- Fix inconsistent results of firstOrNew() when using withCasts() by @Attia-Ahmed ([#41257](https://github.com/laravel/framework/pull/41257))
+- Fix implicitBinding and withTrashed route with child with no SoftDeletes trait by @stein-j ([#41282](https://github.com/laravel/framework/pull/41282))
+
+### Changed
+
+- Unset Connection Resolver extended callback by @emrancu ([#41216](https://github.com/laravel/framework/pull/41216))
+- Update Mailgun transport type by @driesvints ([#41255](https://github.com/laravel/framework/pull/41255))
+
+## [v9.2.0 (2022-02-22)](https://github.com/laravel/framework/compare/v9.1.0...v9.2.0)
+
+### Added
+
+- Added `Illuminate/Database/Eloquent/Casts/Attribute::make()` ([#41014](https://github.com/laravel/framework/pull/41014))
+- Added `Illuminate/Collections/Arr::keyBy()` ([#41029](https://github.com/laravel/framework/pull/41029))
+- Added expectsOutputToContain to the PendingCommand. ([#40984](https://github.com/laravel/framework/pull/40984))
+- Added ability to supply HTTP client methods with JsonSerializable instances ([#41055](https://github.com/laravel/framework/pull/41055))
+- Added `Illuminate/Filesystem/AwsS3V3Adapter::getClient()` ([#41079](https://github.com/laravel/framework/pull/41079))
+- Added Support for enum in Builder::whereRelation ([#41091](https://github.com/laravel/framework/pull/41091))
+- Added X headers when using Mail::alwaysTo ([#41101](https://github.com/laravel/framework/pull/41101))
+- Added of support Bitwise operators in query ([#41112](https://github.com/laravel/framework/pull/41112))
+- Integrate Laravel CORS into framework ([#41137](https://github.com/laravel/framework/pull/41137))
+- Added `Illuminate/Support/Str::betweenFirst()` ([#41144](https://github.com/laravel/framework/pull/41144))
+- Allow specifiying custom messages for Rule objects ([#41145](https://github.com/laravel/framework/pull/41145))
+
+### Fixed
+
+- Fixed Queue Failed_jobs insert issue with Exception contain UNICODE ([#41020](https://github.com/laravel/framework/pull/41020))
+- Fixes attempt to log deprecations on mocks ([#41057](https://github.com/laravel/framework/pull/41057))
+- Fixed loadAggregate not correctly applying casts ([#41050](https://github.com/laravel/framework/pull/41050))
+- Do not transform JsonSerializable instances to array in HTTP client methods ([#41077](https://github.com/laravel/framework/pull/41077))
+- Fix parsing config('database.connections.pgsql.search_path') ([#41088](https://github.com/laravel/framework/pull/41088))
+- Eloquent: firstWhere returns Object instead of NULL ([#41099](https://github.com/laravel/framework/pull/41099))
+- Fixed updated with provided qualified updated_at ([#41133](https://github.com/laravel/framework/pull/41133))
+- Fix setPriority Call for MailChannel ([#41120](https://github.com/laravel/framework/pull/41120))
+- Fixed route:list command output ([#41177](https://github.com/laravel/framework/pull/41177))
+- Fix database migrations $connection property ([#41161](https://github.com/laravel/framework/pull/41161))
+
+### Changed
+
+- Cursor pagination: convert original column to expression ([#41003](https://github.com/laravel/framework/pull/41003))
+- Cast $perPage to integer on Paginator ([#41073](https://github.com/laravel/framework/pull/41073))
+- Restore S3 client extra options ([#41097](https://github.com/laravel/framework/pull/41097))
+- Use `latest()` within `notifications()` in `Illuminate/Notifications/HasDatabaseNotifications.php` ([#41095](https://github.com/laravel/framework/pull/41095))
+- Remove duplicate queries to find batch ([#41121](https://github.com/laravel/framework/pull/41121))
+- Remove redundant check in FormRequest::validated() ([#41115](https://github.com/laravel/framework/pull/41115))
+- Illuminate/Support/Facades/Storage::fake() changed ([#41113](https://github.com/laravel/framework/pull/41113))
+- Use coalesce equal as provided by PHP >= 7.4 ([#41174](https://github.com/laravel/framework/pull/41174))
+- Simplify some conditions with is_countable() ([#41168](https://github.com/laravel/framework/pull/41168))
+- Pass AWS temporary URL options to createPresignedRequest method ([#41156](https://github.com/laravel/framework/pull/41156))
+- Let Multiple* exceptions hold the number of records and items found ([#41164](https://github.com/laravel/framework/pull/41164))
+
+## [v9.1.0 (2022-02-15)](https://github.com/laravel/framework/compare/v9.0.2...v9.1.0)
+
+### Added
+
+- Added the ability to use the uniqueFor method for Jobs by @andrey-helldar in https://github.com/laravel/framework/pull/40974
+- Add filtering of route:list by domain by @Synchro in https://github.com/laravel/framework/pull/40970
+- Added dropForeignIdFor method to match foreignIdFor method by @bretto36 in https://github.com/laravel/framework/pull/40950
+- Adds `Str::excerpt` by @nunomaduro in https://github.com/laravel/framework/pull/41000
+- Make:model --morph flag to generate MorphPivot model by @michael-rubel in https://github.com/laravel/framework/pull/41011
+- Add doesntContain to higher order proxies by @edemots in https://github.com/laravel/framework/pull/41034
+
+### Changed
+
+- Improve types on model factory methods by @axlon in https://github.com/laravel/framework/pull/40902
+- Add support for passing array as the second parameter for the group method. by @hossein-zare in https://github.com/laravel/framework/pull/40945
+- Makes `ExceptionHandler::renderForConsole` internal on contract by @nunomaduro in https://github.com/laravel/framework/pull/40956
+- Put the error message at the bottom of the exceptions by @nshiro in https://github.com/laravel/framework/pull/40886
+- Expose next and previous cursor of cursor paginator by @gdebrauwer in https://github.com/laravel/framework/pull/41001
+
+### Fixed
+
+- Fix FTP root config by @driesvints in https://github.com/laravel/framework/pull/40939
+- Allows tls encryption to be used with port different than 465 with starttls by @nicolalazzaro in https://github.com/laravel/framework/pull/40943
+- Catch suppressed deprecation logs by @nunomaduro in https://github.com/laravel/framework/pull/40942
+- Fix typo in method documentation by @shadman-ahmed in https://github.com/laravel/framework/pull/40951
+- Patch regex rule parsing due to `Rule::forEach()` by @stevebauman in https://github.com/laravel/framework/pull/40941
+- Fix replacing request options by @driesvints in https://github.com/laravel/framework/pull/40954
+- Fix `MessageSent` event by @driesvints in https://github.com/laravel/framework/pull/40963
+- Add firstOr() function to BelongsToMany relation by @r-kujawa in https://github.com/laravel/framework/pull/40828
+- Fix `isRelation()` failing to check an `Attribute` by @rodrigopedra in https://github.com/laravel/framework/pull/40967
+- Fix default pivot attributes by @iamgergo in https://github.com/laravel/framework/pull/40947
+- Fix enum casts arrayable behaviour by @diegotibi in https://github.com/laravel/framework/pull/40885
+- Solve exception error: Undefined array key "", in artisan route:list command by @manuglopez in https://github.com/laravel/framework/pull/41031
+- Fix Duplicate Route Namespace by @moisish in https://github.com/laravel/framework/pull/41021
+- Fix the error message when no routes are detected by @LukeTowers in https://github.com/laravel/framework/pull/41017
+- Fix mails with tags and metadata are not queuable by @joostdebruijn in https://github.com/laravel/framework/pull/41028
+
+## [v9.0.2 (2022-02-10)](https://github.com/laravel/framework/compare/v9.0.1...v9.0.2)
+
+### Added
+
+- Add disabled directive by @belzaaron in https://github.com/laravel/framework/pull/40900
+
+### Changed
+
+- Widen the type of `Collection::unique` `$key` parameter by @NiclasvanEyk in https://github.com/laravel/framework/pull/40903
+- Makes `ExceptionHandler::renderForConsole` internal by @nunomaduro in https://github.com/laravel/framework/pull/40936
+- Removal of Google Font integration from default exception templates by @bashgeek in https://github.com/laravel/framework/pull/40926
+- Allow base JsonResource class to be collected by @jwohlfert23 in https://github.com/laravel/framework/pull/40896
+
+### Fixed
+
+- Fix Support\Collection reject method type definition by @joecampo in https://github.com/laravel/framework/pull/40899
+- Fix SpoofCheckValidation namespace change by @eduardor2k in https://github.com/laravel/framework/pull/40923
+- Fix notification email recipient by @driesvints in https://github.com/laravel/framework/pull/40921
+- Fix publishing visibility by @driesvints in https://github.com/laravel/framework/pull/40918
+- Fix Mailable->priority() by @giggsey in https://github.com/laravel/framework/pull/40917
+
+## [v9.0.1 (2022-02-09)](https://github.com/laravel/framework/compare/v9.0.0...v9.0.1)
+
+### Changed
+
+- Improves `Support\Collection` each method type definition by @zingimmick in https://github.com/laravel/framework/pull/40879
+
+### Fixed
+
+- Update Mailable.php by @rentalhost in https://github.com/laravel/framework/pull/40868
+- Switch to null coalescing operator in Conditionable by @inxilpro in https://github.com/laravel/framework/pull/40888
+- Bring back old return behaviour by @ankurk91 in https://github.com/laravel/framework/pull/40880
+
+## [v9.0.0 (2022-02-08)](https://github.com/laravel/framework/compare/8.x...v9.0.0)
+
+Check the upgrade guide in the [Official Laravel Upgrade Documentation](https://laravel.com/docs/9.x/upgrade). Also you can see some release notes in the [Official Laravel Release Documentation](https://laravel.com/docs/9.x/releases).
diff --git a/vendor/laravel/framework/LICENSE.md b/vendor/laravel/framework/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/laravel/framework/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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.
diff --git a/vendor/laravel/framework/README.md b/vendor/laravel/framework/README.md
new file mode 100644
index 0000000..6e9702b
--- /dev/null
+++ b/vendor/laravel/framework/README.md
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+## About Laravel
+
+> **Note:** This repository contains the core code of the Laravel framework. If you want to build an application using Laravel, visit the main [Laravel repository](https://github.com/laravel/laravel).
+
+Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as:
+
+- [Simple, fast routing engine](https://laravel.com/docs/routing).
+- [Powerful dependency injection container](https://laravel.com/docs/container).
+- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
+- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
+- [Robust background job processing](https://laravel.com/docs/queues).
+- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
+
+Laravel is accessible, yet powerful, providing tools needed for large, robust applications. A superb combination of simplicity, elegance, and innovation gives you a complete toolset required to build any application with which you are tasked.
+
+## Learning Laravel
+
+Laravel has the most extensive and thorough documentation and video tutorial library of any modern web application framework. The [Laravel documentation](https://laravel.com/docs) is in-depth and complete, making it a breeze to get started learning the framework.
+
+If you're not in the mood to read, [Laracasts](https://laracasts.com) contains over 1100 video tutorials covering a range of topics including Laravel, modern PHP, unit testing, JavaScript, and more. Boost the skill level of yourself and your entire team by digging into our comprehensive video library.
+
+## Contributing
+
+Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
+
+## Code of Conduct
+
+In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
+
+## Security Vulnerabilities
+
+Please review [our security policy](https://github.com/laravel/framework/security/policy) on how to report security vulnerabilities.
+
+## License
+
+The Laravel framework is open-sourced software licensed under the [MIT license](LICENSE.md).
diff --git a/vendor/laravel/framework/composer.json b/vendor/laravel/framework/composer.json
new file mode 100644
index 0000000..74f27ac
--- /dev/null
+++ b/vendor/laravel/framework/composer.json
@@ -0,0 +1,175 @@
+{
+ "name": "laravel/framework",
+ "description": "The Laravel Framework.",
+ "keywords": ["framework", "laravel"],
+ "license": "MIT",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^8.0.2",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "doctrine/inflector": "^2.0",
+ "dragonmantank/cron-expression": "^3.1",
+ "egulias/email-validator": "^3.1",
+ "fruitcake/php-cors": "^1.2",
+ "laravel/serializable-closure": "^1.0",
+ "league/commonmark": "^2.2",
+ "league/flysystem": "^3.0.16",
+ "monolog/monolog": "^2.0",
+ "nesbot/carbon": "^2.53.1",
+ "psr/container": "^1.1.1|^2.0.1",
+ "psr/log": "^1.0|^2.0|^3.0",
+ "psr/simple-cache": "^1.0|^2.0|^3.0",
+ "ramsey/uuid": "^4.2.2",
+ "symfony/console": "^6.0",
+ "symfony/error-handler": "^6.0",
+ "symfony/finder": "^6.0",
+ "symfony/http-foundation": "^6.0",
+ "symfony/http-kernel": "^6.0",
+ "symfony/mailer": "^6.0",
+ "symfony/mime": "^6.0",
+ "symfony/process": "^6.0",
+ "symfony/routing": "^6.0",
+ "symfony/var-dumper": "^6.0",
+ "tijsverkoyen/css-to-inline-styles": "^2.2.2",
+ "vlucas/phpdotenv": "^5.4.1",
+ "voku/portable-ascii": "^2.0"
+ },
+ "replace": {
+ "illuminate/auth": "self.version",
+ "illuminate/broadcasting": "self.version",
+ "illuminate/bus": "self.version",
+ "illuminate/cache": "self.version",
+ "illuminate/collections": "self.version",
+ "illuminate/conditionable": "self.version",
+ "illuminate/config": "self.version",
+ "illuminate/console": "self.version",
+ "illuminate/container": "self.version",
+ "illuminate/contracts": "self.version",
+ "illuminate/cookie": "self.version",
+ "illuminate/database": "self.version",
+ "illuminate/encryption": "self.version",
+ "illuminate/events": "self.version",
+ "illuminate/filesystem": "self.version",
+ "illuminate/hashing": "self.version",
+ "illuminate/http": "self.version",
+ "illuminate/log": "self.version",
+ "illuminate/macroable": "self.version",
+ "illuminate/mail": "self.version",
+ "illuminate/notifications": "self.version",
+ "illuminate/pagination": "self.version",
+ "illuminate/pipeline": "self.version",
+ "illuminate/queue": "self.version",
+ "illuminate/redis": "self.version",
+ "illuminate/routing": "self.version",
+ "illuminate/session": "self.version",
+ "illuminate/support": "self.version",
+ "illuminate/testing": "self.version",
+ "illuminate/translation": "self.version",
+ "illuminate/validation": "self.version",
+ "illuminate/view": "self.version"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^3.198.1",
+ "doctrine/dbal": "^2.13.3|^3.1.4",
+ "fakerphp/faker": "^1.9.2",
+ "guzzlehttp/guzzle": "^7.2",
+ "league/flysystem-aws-s3-v3": "^3.0",
+ "league/flysystem-ftp": "^3.0",
+ "league/flysystem-sftp-v3": "^3.0",
+ "mockery/mockery": "^1.4.4",
+ "orchestra/testbench-core": "^7.1",
+ "pda/pheanstalk": "^4.0",
+ "phpstan/phpstan": "^1.4.7",
+ "phpunit/phpunit": "^9.5.8",
+ "predis/predis": "^1.1.9|^2.0",
+ "symfony/cache": "^6.0"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0",
+ "psr/simple-cache-implementation": "1.0|2.0|3.0"
+ },
+ "conflict": {
+ "tightenco/collect": "<5.5.33"
+ },
+ "autoload": {
+ "files": [
+ "src/Illuminate/Collections/helpers.php",
+ "src/Illuminate/Events/functions.php",
+ "src/Illuminate/Foundation/helpers.php",
+ "src/Illuminate/Support/helpers.php"
+ ],
+ "psr-4": {
+ "Illuminate\\": "src/Illuminate/",
+ "Illuminate\\Support\\": [
+ "src/Illuminate/Macroable/",
+ "src/Illuminate/Collections/",
+ "src/Illuminate/Conditionable/"
+ ]
+ }
+ },
+ "autoload-dev": {
+ "files": [
+ "tests/Database/stubs/MigrationCreatorFakeMigration.php"
+ ],
+ "psr-4": {
+ "Illuminate\\Tests\\": "tests/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "suggest": {
+ "ext-bcmath": "Required to use the multiple_of validation rule.",
+ "ext-ftp": "Required to use the Flysystem FTP driver.",
+ "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
+ "ext-memcached": "Required to use the memcache cache driver.",
+ "ext-pcntl": "Required to use all features of the queue worker.",
+ "ext-posix": "Required to use all features of the queue worker.",
+ "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).",
+ "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
+ "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.198.1).",
+ "brianium/paratest": "Required to run tests in parallel (^6.0).",
+ "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).",
+ "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).",
+ "filp/whoops": "Required for friendly error pages in development (^2.14.3).",
+ "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.2).",
+ "laravel/tinker": "Required to use the tinker console command (^2.0).",
+ "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).",
+ "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).",
+ "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).",
+ "mockery/mockery": "Required to use mocking (^1.4.4).",
+ "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
+ "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
+ "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8).",
+ "predis/predis": "Required to use the predis connector (^1.1.9|^2.0).",
+ "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
+ "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).",
+ "symfony/cache": "Required to PSR-6 cache bridge (^6.0).",
+ "symfony/filesystem": "Required to enable support for relative symbolic links (^6.0).",
+ "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.0).",
+ "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.0).",
+ "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.0).",
+ "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)."
+ },
+ "config": {
+ "sort-packages": true,
+ "allow-plugins": {
+ "composer/package-versions-deprecated": true
+ }
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Access/AuthorizationException.php b/vendor/laravel/framework/src/Illuminate/Auth/Access/AuthorizationException.php
new file mode 100644
index 0000000..17101b4
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Access/AuthorizationException.php
@@ -0,0 +1,114 @@
+code = $code ?: 0;
+ }
+
+ /**
+ * Get the response from the gate.
+ *
+ * @return \Illuminate\Auth\Access\Response
+ */
+ public function response()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Set the response from the gate.
+ *
+ * @param \Illuminate\Auth\Access\Response $response
+ * @return $this
+ */
+ public function setResponse($response)
+ {
+ $this->response = $response;
+
+ return $this;
+ }
+
+ /**
+ * Set the HTTP response status code.
+ *
+ * @param int|null $status
+ * @return $this
+ */
+ public function withStatus($status)
+ {
+ $this->status = $status;
+
+ return $this;
+ }
+
+ /**
+ * Set the HTTP response status code to 404.
+ *
+ * @return $this
+ */
+ public function asNotFound()
+ {
+ return $this->withStatus(404);
+ }
+
+ /**
+ * Determine if the HTTP status code has been set.
+ *
+ * @return bool
+ */
+ public function hasStatus()
+ {
+ return $this->status !== null;
+ }
+
+ /**
+ * Get the HTTP status code.
+ *
+ * @return int|null
+ */
+ public function status()
+ {
+ return $this->status;
+ }
+
+ /**
+ * Create a deny response object from this exception.
+ *
+ * @return \Illuminate\Auth\Access\Response
+ */
+ public function toResponse()
+ {
+ return Response::deny($this->message, $this->code)->withStatus($this->status);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Access/Events/GateEvaluated.php b/vendor/laravel/framework/src/Illuminate/Auth/Access/Events/GateEvaluated.php
new file mode 100644
index 0000000..f77a9c8
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Access/Events/GateEvaluated.php
@@ -0,0 +1,51 @@
+user = $user;
+ $this->ability = $ability;
+ $this->result = $result;
+ $this->arguments = $arguments;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php b/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php
new file mode 100644
index 0000000..1694607
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Access/Gate.php
@@ -0,0 +1,872 @@
+policies = $policies;
+ $this->container = $container;
+ $this->abilities = $abilities;
+ $this->userResolver = $userResolver;
+ $this->afterCallbacks = $afterCallbacks;
+ $this->beforeCallbacks = $beforeCallbacks;
+ $this->guessPolicyNamesUsingCallback = $guessPolicyNamesUsingCallback;
+ }
+
+ /**
+ * Determine if a given ability has been defined.
+ *
+ * @param string|array $ability
+ * @return bool
+ */
+ public function has($ability)
+ {
+ $abilities = is_array($ability) ? $ability : func_get_args();
+
+ foreach ($abilities as $ability) {
+ if (! isset($this->abilities[$ability])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Perform an on-demand authorization check. Throw an authorization exception if the condition or callback is false.
+ *
+ * @param \Illuminate\Auth\Access\Response|\Closure|bool $condition
+ * @param string|null $message
+ * @param string|null $code
+ * @return \Illuminate\Auth\Access\Response
+ *
+ * @throws \Illuminate\Auth\Access\AuthorizationException
+ */
+ public function allowIf($condition, $message = null, $code = null)
+ {
+ return $this->authorizeOnDemand($condition, $message, $code, true);
+ }
+
+ /**
+ * Perform an on-demand authorization check. Throw an authorization exception if the condition or callback is true.
+ *
+ * @param \Illuminate\Auth\Access\Response|\Closure|bool $condition
+ * @param string|null $message
+ * @param string|null $code
+ * @return \Illuminate\Auth\Access\Response
+ *
+ * @throws \Illuminate\Auth\Access\AuthorizationException
+ */
+ public function denyIf($condition, $message = null, $code = null)
+ {
+ return $this->authorizeOnDemand($condition, $message, $code, false);
+ }
+
+ /**
+ * Authorize a given condition or callback.
+ *
+ * @param \Illuminate\Auth\Access\Response|\Closure|bool $condition
+ * @param string|null $message
+ * @param string|null $code
+ * @param bool $allowWhenResponseIs
+ * @return \Illuminate\Auth\Access\Response
+ *
+ * @throws \Illuminate\Auth\Access\AuthorizationException
+ */
+ protected function authorizeOnDemand($condition, $message, $code, $allowWhenResponseIs)
+ {
+ $user = $this->resolveUser();
+
+ if ($condition instanceof Closure) {
+ $response = $this->canBeCalledWithUser($user, $condition)
+ ? $condition($user)
+ : new Response(false, $message, $code);
+ } else {
+ $response = $condition;
+ }
+
+ return with($response instanceof Response ? $response : new Response(
+ (bool) $response === $allowWhenResponseIs, $message, $code
+ ))->authorize();
+ }
+
+ /**
+ * Define a new ability.
+ *
+ * @param string $ability
+ * @param callable|array|string $callback
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function define($ability, $callback)
+ {
+ if (is_array($callback) && isset($callback[0]) && is_string($callback[0])) {
+ $callback = $callback[0].'@'.$callback[1];
+ }
+
+ if (is_callable($callback)) {
+ $this->abilities[$ability] = $callback;
+ } elseif (is_string($callback)) {
+ $this->stringCallbacks[$ability] = $callback;
+
+ $this->abilities[$ability] = $this->buildAbilityCallback($ability, $callback);
+ } else {
+ throw new InvalidArgumentException("Callback must be a callable, callback array, or a 'Class@method' string.");
+ }
+
+ return $this;
+ }
+
+ /**
+ * Define abilities for a resource.
+ *
+ * @param string $name
+ * @param string $class
+ * @param array|null $abilities
+ * @return $this
+ */
+ public function resource($name, $class, array $abilities = null)
+ {
+ $abilities = $abilities ?: [
+ 'viewAny' => 'viewAny',
+ 'view' => 'view',
+ 'create' => 'create',
+ 'update' => 'update',
+ 'delete' => 'delete',
+ ];
+
+ foreach ($abilities as $ability => $method) {
+ $this->define($name.'.'.$ability, $class.'@'.$method);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Create the ability callback for a callback string.
+ *
+ * @param string $ability
+ * @param string $callback
+ * @return \Closure
+ */
+ protected function buildAbilityCallback($ability, $callback)
+ {
+ return function () use ($ability, $callback) {
+ if (str_contains($callback, '@')) {
+ [$class, $method] = Str::parseCallback($callback);
+ } else {
+ $class = $callback;
+ }
+
+ $policy = $this->resolvePolicy($class);
+
+ $arguments = func_get_args();
+
+ $user = array_shift($arguments);
+
+ $result = $this->callPolicyBefore(
+ $policy, $user, $ability, $arguments
+ );
+
+ if (! is_null($result)) {
+ return $result;
+ }
+
+ return isset($method)
+ ? $policy->{$method}(...func_get_args())
+ : $policy(...func_get_args());
+ };
+ }
+
+ /**
+ * Define a policy class for a given class type.
+ *
+ * @param string $class
+ * @param string $policy
+ * @return $this
+ */
+ public function policy($class, $policy)
+ {
+ $this->policies[$class] = $policy;
+
+ return $this;
+ }
+
+ /**
+ * Register a callback to run before all Gate checks.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function before(callable $callback)
+ {
+ $this->beforeCallbacks[] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Register a callback to run after all Gate checks.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function after(callable $callback)
+ {
+ $this->afterCallbacks[] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Determine if the given ability should be granted for the current user.
+ *
+ * @param string $ability
+ * @param array|mixed $arguments
+ * @return bool
+ */
+ public function allows($ability, $arguments = [])
+ {
+ return $this->check($ability, $arguments);
+ }
+
+ /**
+ * Determine if the given ability should be denied for the current user.
+ *
+ * @param string $ability
+ * @param array|mixed $arguments
+ * @return bool
+ */
+ public function denies($ability, $arguments = [])
+ {
+ return ! $this->allows($ability, $arguments);
+ }
+
+ /**
+ * Determine if all of the given abilities should be granted for the current user.
+ *
+ * @param iterable|string $abilities
+ * @param array|mixed $arguments
+ * @return bool
+ */
+ public function check($abilities, $arguments = [])
+ {
+ return collect($abilities)->every(
+ fn ($ability) => $this->inspect($ability, $arguments)->allowed()
+ );
+ }
+
+ /**
+ * Determine if any one of the given abilities should be granted for the current user.
+ *
+ * @param iterable|string $abilities
+ * @param array|mixed $arguments
+ * @return bool
+ */
+ public function any($abilities, $arguments = [])
+ {
+ return collect($abilities)->contains(fn ($ability) => $this->check($ability, $arguments));
+ }
+
+ /**
+ * Determine if all of the given abilities should be denied for the current user.
+ *
+ * @param iterable|string $abilities
+ * @param array|mixed $arguments
+ * @return bool
+ */
+ public function none($abilities, $arguments = [])
+ {
+ return ! $this->any($abilities, $arguments);
+ }
+
+ /**
+ * Determine if the given ability should be granted for the current user.
+ *
+ * @param string $ability
+ * @param array|mixed $arguments
+ * @return \Illuminate\Auth\Access\Response
+ *
+ * @throws \Illuminate\Auth\Access\AuthorizationException
+ */
+ public function authorize($ability, $arguments = [])
+ {
+ return $this->inspect($ability, $arguments)->authorize();
+ }
+
+ /**
+ * Inspect the user for the given ability.
+ *
+ * @param string $ability
+ * @param array|mixed $arguments
+ * @return \Illuminate\Auth\Access\Response
+ */
+ public function inspect($ability, $arguments = [])
+ {
+ try {
+ $result = $this->raw($ability, $arguments);
+
+ if ($result instanceof Response) {
+ return $result;
+ }
+
+ return $result ? Response::allow() : Response::deny();
+ } catch (AuthorizationException $e) {
+ return $e->toResponse();
+ }
+ }
+
+ /**
+ * Get the raw result from the authorization callback.
+ *
+ * @param string $ability
+ * @param array|mixed $arguments
+ * @return mixed
+ *
+ * @throws \Illuminate\Auth\Access\AuthorizationException
+ */
+ public function raw($ability, $arguments = [])
+ {
+ $arguments = Arr::wrap($arguments);
+
+ $user = $this->resolveUser();
+
+ // First we will call the "before" callbacks for the Gate. If any of these give
+ // back a non-null response, we will immediately return that result in order
+ // to let the developers override all checks for some authorization cases.
+ $result = $this->callBeforeCallbacks(
+ $user, $ability, $arguments
+ );
+
+ if (is_null($result)) {
+ $result = $this->callAuthCallback($user, $ability, $arguments);
+ }
+
+ // After calling the authorization callback, we will call the "after" callbacks
+ // that are registered with the Gate, which allows a developer to do logging
+ // if that is required for this application. Then we'll return the result.
+ return tap($this->callAfterCallbacks(
+ $user, $ability, $arguments, $result
+ ), function ($result) use ($user, $ability, $arguments) {
+ $this->dispatchGateEvaluatedEvent($user, $ability, $arguments, $result);
+ });
+ }
+
+ /**
+ * Determine whether the callback/method can be called with the given user.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable|null $user
+ * @param \Closure|string|array $class
+ * @param string|null $method
+ * @return bool
+ */
+ protected function canBeCalledWithUser($user, $class, $method = null)
+ {
+ if (! is_null($user)) {
+ return true;
+ }
+
+ if (! is_null($method)) {
+ return $this->methodAllowsGuests($class, $method);
+ }
+
+ if (is_array($class)) {
+ $className = is_string($class[0]) ? $class[0] : get_class($class[0]);
+
+ return $this->methodAllowsGuests($className, $class[1]);
+ }
+
+ return $this->callbackAllowsGuests($class);
+ }
+
+ /**
+ * Determine if the given class method allows guests.
+ *
+ * @param string $class
+ * @param string $method
+ * @return bool
+ */
+ protected function methodAllowsGuests($class, $method)
+ {
+ try {
+ $reflection = new ReflectionClass($class);
+
+ $method = $reflection->getMethod($method);
+ } catch (Exception $e) {
+ return false;
+ }
+
+ if ($method) {
+ $parameters = $method->getParameters();
+
+ return isset($parameters[0]) && $this->parameterAllowsGuests($parameters[0]);
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine if the callback allows guests.
+ *
+ * @param callable $callback
+ * @return bool
+ *
+ * @throws \ReflectionException
+ */
+ protected function callbackAllowsGuests($callback)
+ {
+ $parameters = (new ReflectionFunction($callback))->getParameters();
+
+ return isset($parameters[0]) && $this->parameterAllowsGuests($parameters[0]);
+ }
+
+ /**
+ * Determine if the given parameter allows guests.
+ *
+ * @param \ReflectionParameter $parameter
+ * @return bool
+ */
+ protected function parameterAllowsGuests($parameter)
+ {
+ return ($parameter->hasType() && $parameter->allowsNull()) ||
+ ($parameter->isDefaultValueAvailable() && is_null($parameter->getDefaultValue()));
+ }
+
+ /**
+ * Resolve and call the appropriate authorization callback.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable|null $user
+ * @param string $ability
+ * @param array $arguments
+ * @return bool
+ */
+ protected function callAuthCallback($user, $ability, array $arguments)
+ {
+ $callback = $this->resolveAuthCallback($user, $ability, $arguments);
+
+ return $callback($user, ...$arguments);
+ }
+
+ /**
+ * Call all of the before callbacks and return if a result is given.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable|null $user
+ * @param string $ability
+ * @param array $arguments
+ * @return bool|null
+ */
+ protected function callBeforeCallbacks($user, $ability, array $arguments)
+ {
+ foreach ($this->beforeCallbacks as $before) {
+ if (! $this->canBeCalledWithUser($user, $before)) {
+ continue;
+ }
+
+ if (! is_null($result = $before($user, $ability, $arguments))) {
+ return $result;
+ }
+ }
+ }
+
+ /**
+ * Call all of the after callbacks with check result.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param string $ability
+ * @param array $arguments
+ * @param bool $result
+ * @return bool|null
+ */
+ protected function callAfterCallbacks($user, $ability, array $arguments, $result)
+ {
+ foreach ($this->afterCallbacks as $after) {
+ if (! $this->canBeCalledWithUser($user, $after)) {
+ continue;
+ }
+
+ $afterResult = $after($user, $ability, $result, $arguments);
+
+ $result ??= $afterResult;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Dispatch a gate evaluation event.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable|null $user
+ * @param string $ability
+ * @param array $arguments
+ * @param bool|null $result
+ * @return void
+ */
+ protected function dispatchGateEvaluatedEvent($user, $ability, array $arguments, $result)
+ {
+ if ($this->container->bound(Dispatcher::class)) {
+ $this->container->make(Dispatcher::class)->dispatch(
+ new GateEvaluated($user, $ability, $result, $arguments)
+ );
+ }
+ }
+
+ /**
+ * Resolve the callable for the given ability and arguments.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable|null $user
+ * @param string $ability
+ * @param array $arguments
+ * @return callable
+ */
+ protected function resolveAuthCallback($user, $ability, array $arguments)
+ {
+ if (isset($arguments[0]) &&
+ ! is_null($policy = $this->getPolicyFor($arguments[0])) &&
+ $callback = $this->resolvePolicyCallback($user, $ability, $arguments, $policy)) {
+ return $callback;
+ }
+
+ if (isset($this->stringCallbacks[$ability])) {
+ [$class, $method] = Str::parseCallback($this->stringCallbacks[$ability]);
+
+ if ($this->canBeCalledWithUser($user, $class, $method ?: '__invoke')) {
+ return $this->abilities[$ability];
+ }
+ }
+
+ if (isset($this->abilities[$ability]) &&
+ $this->canBeCalledWithUser($user, $this->abilities[$ability])) {
+ return $this->abilities[$ability];
+ }
+
+ return function () {
+ //
+ };
+ }
+
+ /**
+ * Get a policy instance for a given class.
+ *
+ * @param object|string $class
+ * @return mixed
+ */
+ public function getPolicyFor($class)
+ {
+ if (is_object($class)) {
+ $class = get_class($class);
+ }
+
+ if (! is_string($class)) {
+ return;
+ }
+
+ if (isset($this->policies[$class])) {
+ return $this->resolvePolicy($this->policies[$class]);
+ }
+
+ foreach ($this->guessPolicyName($class) as $guessedPolicy) {
+ if (class_exists($guessedPolicy)) {
+ return $this->resolvePolicy($guessedPolicy);
+ }
+ }
+
+ foreach ($this->policies as $expected => $policy) {
+ if (is_subclass_of($class, $expected)) {
+ return $this->resolvePolicy($policy);
+ }
+ }
+ }
+
+ /**
+ * Guess the policy name for the given class.
+ *
+ * @param string $class
+ * @return array
+ */
+ protected function guessPolicyName($class)
+ {
+ if ($this->guessPolicyNamesUsingCallback) {
+ return Arr::wrap(call_user_func($this->guessPolicyNamesUsingCallback, $class));
+ }
+
+ $classDirname = str_replace('/', '\\', dirname(str_replace('\\', '/', $class)));
+
+ $classDirnameSegments = explode('\\', $classDirname);
+
+ return Arr::wrap(Collection::times(count($classDirnameSegments), function ($index) use ($class, $classDirnameSegments) {
+ $classDirname = implode('\\', array_slice($classDirnameSegments, 0, $index));
+
+ return $classDirname.'\\Policies\\'.class_basename($class).'Policy';
+ })->reverse()->values()->first(function ($class) {
+ return class_exists($class);
+ }) ?: [$classDirname.'\\Policies\\'.class_basename($class).'Policy']);
+ }
+
+ /**
+ * Specify a callback to be used to guess policy names.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function guessPolicyNamesUsing(callable $callback)
+ {
+ $this->guessPolicyNamesUsingCallback = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Build a policy class instance of the given type.
+ *
+ * @param object|string $class
+ * @return mixed
+ *
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
+ */
+ public function resolvePolicy($class)
+ {
+ return $this->container->make($class);
+ }
+
+ /**
+ * Resolve the callback for a policy check.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param string $ability
+ * @param array $arguments
+ * @param mixed $policy
+ * @return bool|callable
+ */
+ protected function resolvePolicyCallback($user, $ability, array $arguments, $policy)
+ {
+ if (! is_callable([$policy, $this->formatAbilityToMethod($ability)])) {
+ return false;
+ }
+
+ return function () use ($user, $ability, $arguments, $policy) {
+ // This callback will be responsible for calling the policy's before method and
+ // running this policy method if necessary. This is used to when objects are
+ // mapped to policy objects in the user's configurations or on this class.
+ $result = $this->callPolicyBefore(
+ $policy, $user, $ability, $arguments
+ );
+
+ // When we receive a non-null result from this before method, we will return it
+ // as the "final" results. This will allow developers to override the checks
+ // in this policy to return the result for all rules defined in the class.
+ if (! is_null($result)) {
+ return $result;
+ }
+
+ $method = $this->formatAbilityToMethod($ability);
+
+ return $this->callPolicyMethod($policy, $method, $user, $arguments);
+ };
+ }
+
+ /**
+ * Call the "before" method on the given policy, if applicable.
+ *
+ * @param mixed $policy
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param string $ability
+ * @param array $arguments
+ * @return mixed
+ */
+ protected function callPolicyBefore($policy, $user, $ability, $arguments)
+ {
+ if (! method_exists($policy, 'before')) {
+ return;
+ }
+
+ if ($this->canBeCalledWithUser($user, $policy, 'before')) {
+ return $policy->before($user, $ability, ...$arguments);
+ }
+ }
+
+ /**
+ * Call the appropriate method on the given policy.
+ *
+ * @param mixed $policy
+ * @param string $method
+ * @param \Illuminate\Contracts\Auth\Authenticatable|null $user
+ * @param array $arguments
+ * @return mixed
+ */
+ protected function callPolicyMethod($policy, $method, $user, array $arguments)
+ {
+ // If this first argument is a string, that means they are passing a class name
+ // to the policy. We will remove the first argument from this argument array
+ // because this policy already knows what type of models it can authorize.
+ if (isset($arguments[0]) && is_string($arguments[0])) {
+ array_shift($arguments);
+ }
+
+ if (! is_callable([$policy, $method])) {
+ return;
+ }
+
+ if ($this->canBeCalledWithUser($user, $policy, $method)) {
+ return $policy->{$method}($user, ...$arguments);
+ }
+ }
+
+ /**
+ * Format the policy ability into a method name.
+ *
+ * @param string $ability
+ * @return string
+ */
+ protected function formatAbilityToMethod($ability)
+ {
+ return str_contains($ability, '-') ? Str::camel($ability) : $ability;
+ }
+
+ /**
+ * Get a gate instance for the given user.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable|mixed $user
+ * @return static
+ */
+ public function forUser($user)
+ {
+ $callback = fn () => $user;
+
+ return new static(
+ $this->container, $callback, $this->abilities,
+ $this->policies, $this->beforeCallbacks, $this->afterCallbacks,
+ $this->guessPolicyNamesUsingCallback
+ );
+ }
+
+ /**
+ * Resolve the user from the user resolver.
+ *
+ * @return mixed
+ */
+ protected function resolveUser()
+ {
+ return call_user_func($this->userResolver);
+ }
+
+ /**
+ * Get all of the defined abilities.
+ *
+ * @return array
+ */
+ public function abilities()
+ {
+ return $this->abilities;
+ }
+
+ /**
+ * Get all of the defined policies.
+ *
+ * @return array
+ */
+ public function policies()
+ {
+ return $this->policies;
+ }
+
+ /**
+ * Set the container instance used by the gate.
+ *
+ * @param \Illuminate\Contracts\Container\Container $container
+ * @return $this
+ */
+ public function setContainer(Container $container)
+ {
+ $this->container = $container;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Access/HandlesAuthorization.php b/vendor/laravel/framework/src/Illuminate/Auth/Access/HandlesAuthorization.php
new file mode 100644
index 0000000..fc71cf1
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Access/HandlesAuthorization.php
@@ -0,0 +1,55 @@
+code = $code;
+ $this->allowed = $allowed;
+ $this->message = $message;
+ }
+
+ /**
+ * Create a new "allow" Response.
+ *
+ * @param string|null $message
+ * @param mixed $code
+ * @return \Illuminate\Auth\Access\Response
+ */
+ public static function allow($message = null, $code = null)
+ {
+ return new static(true, $message, $code);
+ }
+
+ /**
+ * Create a new "deny" Response.
+ *
+ * @param string|null $message
+ * @param mixed $code
+ * @return \Illuminate\Auth\Access\Response
+ */
+ public static function deny($message = null, $code = null)
+ {
+ return new static(false, $message, $code);
+ }
+
+ /**
+ * Create a new "deny" Response with a HTTP status code.
+ *
+ * @param int $status
+ * @param string|null $message
+ * @param mixed $code
+ * @return \Illuminate\Auth\Access\Response
+ */
+ public static function denyWithStatus($status, $message = null, $code = null)
+ {
+ return static::deny($message, $code)->withStatus($status);
+ }
+
+ /**
+ * Create a new "deny" Response with a 404 HTTP status code.
+ *
+ * @param string|null $message
+ * @param mixed $code
+ * @return \Illuminate\Auth\Access\Response
+ */
+ public static function denyAsNotFound($message = null, $code = null)
+ {
+ return static::denyWithStatus(404, $message, $code);
+ }
+
+ /**
+ * Determine if the response was allowed.
+ *
+ * @return bool
+ */
+ public function allowed()
+ {
+ return $this->allowed;
+ }
+
+ /**
+ * Determine if the response was denied.
+ *
+ * @return bool
+ */
+ public function denied()
+ {
+ return ! $this->allowed();
+ }
+
+ /**
+ * Get the response message.
+ *
+ * @return string|null
+ */
+ public function message()
+ {
+ return $this->message;
+ }
+
+ /**
+ * Get the response code / reason.
+ *
+ * @return mixed
+ */
+ public function code()
+ {
+ return $this->code;
+ }
+
+ /**
+ * Throw authorization exception if response was denied.
+ *
+ * @return \Illuminate\Auth\Access\Response
+ *
+ * @throws \Illuminate\Auth\Access\AuthorizationException
+ */
+ public function authorize()
+ {
+ if ($this->denied()) {
+ throw (new AuthorizationException($this->message(), $this->code()))
+ ->setResponse($this)
+ ->withStatus($this->status);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Set the HTTP response status code.
+ *
+ * @param null|int $status
+ * @return $this
+ */
+ public function withStatus($status)
+ {
+ $this->status = $status;
+
+ return $this;
+ }
+
+ /**
+ * Set the HTTP response status code to 404.
+ *
+ * @return $this
+ */
+ public function asNotFound()
+ {
+ return $this->withStatus(404);
+ }
+
+ /**
+ * Get the HTTP status code.
+ *
+ * @return int|null
+ */
+ public function status()
+ {
+ return $this->status;
+ }
+
+ /**
+ * Convert the response to an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return [
+ 'allowed' => $this->allowed(),
+ 'message' => $this->message(),
+ 'code' => $this->code(),
+ ];
+ }
+
+ /**
+ * Get the string representation of the message.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return (string) $this->message();
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php b/vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php
new file mode 100755
index 0000000..cc23eb8
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php
@@ -0,0 +1,334 @@
+app = $app;
+
+ $this->userResolver = fn ($guard = null) => $this->guard($guard)->user();
+ }
+
+ /**
+ * Attempt to get the guard from the local cache.
+ *
+ * @param string|null $name
+ * @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
+ */
+ public function guard($name = null)
+ {
+ $name = $name ?: $this->getDefaultDriver();
+
+ return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
+ }
+
+ /**
+ * Resolve the given guard.
+ *
+ * @param string $name
+ * @return \Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function resolve($name)
+ {
+ $config = $this->getConfig($name);
+
+ if (is_null($config)) {
+ throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
+ }
+
+ if (isset($this->customCreators[$config['driver']])) {
+ return $this->callCustomCreator($name, $config);
+ }
+
+ $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
+
+ if (method_exists($this, $driverMethod)) {
+ return $this->{$driverMethod}($name, $config);
+ }
+
+ throw new InvalidArgumentException(
+ "Auth driver [{$config['driver']}] for guard [{$name}] is not defined."
+ );
+ }
+
+ /**
+ * Call a custom driver creator.
+ *
+ * @param string $name
+ * @param array $config
+ * @return mixed
+ */
+ protected function callCustomCreator($name, array $config)
+ {
+ return $this->customCreators[$config['driver']]($this->app, $name, $config);
+ }
+
+ /**
+ * Create a session based authentication guard.
+ *
+ * @param string $name
+ * @param array $config
+ * @return \Illuminate\Auth\SessionGuard
+ */
+ public function createSessionDriver($name, $config)
+ {
+ $provider = $this->createUserProvider($config['provider'] ?? null);
+
+ $guard = new SessionGuard($name, $provider, $this->app['session.store']);
+
+ // When using the remember me functionality of the authentication services we
+ // will need to be set the encryption instance of the guard, which allows
+ // secure, encrypted cookie values to get generated for those cookies.
+ if (method_exists($guard, 'setCookieJar')) {
+ $guard->setCookieJar($this->app['cookie']);
+ }
+
+ if (method_exists($guard, 'setDispatcher')) {
+ $guard->setDispatcher($this->app['events']);
+ }
+
+ if (method_exists($guard, 'setRequest')) {
+ $guard->setRequest($this->app->refresh('request', $guard, 'setRequest'));
+ }
+
+ if (isset($config['remember'])) {
+ $guard->setRememberDuration($config['remember']);
+ }
+
+ return $guard;
+ }
+
+ /**
+ * Create a token based authentication guard.
+ *
+ * @param string $name
+ * @param array $config
+ * @return \Illuminate\Auth\TokenGuard
+ */
+ public function createTokenDriver($name, $config)
+ {
+ // The token guard implements a basic API token based guard implementation
+ // that takes an API token field from the request and matches it to the
+ // user in the database or another persistence layer where users are.
+ $guard = new TokenGuard(
+ $this->createUserProvider($config['provider'] ?? null),
+ $this->app['request'],
+ $config['input_key'] ?? 'api_token',
+ $config['storage_key'] ?? 'api_token',
+ $config['hash'] ?? false
+ );
+
+ $this->app->refresh('request', $guard, 'setRequest');
+
+ return $guard;
+ }
+
+ /**
+ * Get the guard configuration.
+ *
+ * @param string $name
+ * @return array
+ */
+ protected function getConfig($name)
+ {
+ return $this->app['config']["auth.guards.{$name}"];
+ }
+
+ /**
+ * Get the default authentication driver name.
+ *
+ * @return string
+ */
+ public function getDefaultDriver()
+ {
+ return $this->app['config']['auth.defaults.guard'];
+ }
+
+ /**
+ * Set the default guard driver the factory should serve.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function shouldUse($name)
+ {
+ $name = $name ?: $this->getDefaultDriver();
+
+ $this->setDefaultDriver($name);
+
+ $this->userResolver = fn ($name = null) => $this->guard($name)->user();
+ }
+
+ /**
+ * Set the default authentication driver name.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function setDefaultDriver($name)
+ {
+ $this->app['config']['auth.defaults.guard'] = $name;
+ }
+
+ /**
+ * Register a new callback based request guard.
+ *
+ * @param string $driver
+ * @param callable $callback
+ * @return $this
+ */
+ public function viaRequest($driver, callable $callback)
+ {
+ return $this->extend($driver, function () use ($callback) {
+ $guard = new RequestGuard($callback, $this->app['request'], $this->createUserProvider());
+
+ $this->app->refresh('request', $guard, 'setRequest');
+
+ return $guard;
+ });
+ }
+
+ /**
+ * Get the user resolver callback.
+ *
+ * @return \Closure
+ */
+ public function userResolver()
+ {
+ return $this->userResolver;
+ }
+
+ /**
+ * Set the callback to be used to resolve users.
+ *
+ * @param \Closure $userResolver
+ * @return $this
+ */
+ public function resolveUsersUsing(Closure $userResolver)
+ {
+ $this->userResolver = $userResolver;
+
+ return $this;
+ }
+
+ /**
+ * Register a custom driver creator Closure.
+ *
+ * @param string $driver
+ * @param \Closure $callback
+ * @return $this
+ */
+ public function extend($driver, Closure $callback)
+ {
+ $this->customCreators[$driver] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Register a custom provider creator Closure.
+ *
+ * @param string $name
+ * @param \Closure $callback
+ * @return $this
+ */
+ public function provider($name, Closure $callback)
+ {
+ $this->customProviderCreators[$name] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Determines if any guards have already been resolved.
+ *
+ * @return bool
+ */
+ public function hasResolvedGuards()
+ {
+ return count($this->guards) > 0;
+ }
+
+ /**
+ * Forget all of the resolved guard instances.
+ *
+ * @return $this
+ */
+ public function forgetGuards()
+ {
+ $this->guards = [];
+
+ return $this;
+ }
+
+ /**
+ * Set the application instance used by the manager.
+ *
+ * @param \Illuminate\Contracts\Foundation\Application $app
+ * @return $this
+ */
+ public function setApplication($app)
+ {
+ $this->app = $app;
+
+ return $this;
+ }
+
+ /**
+ * Dynamically call the default driver instance.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->guard()->{$method}(...$parameters);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/AuthServiceProvider.php b/vendor/laravel/framework/src/Illuminate/Auth/AuthServiceProvider.php
new file mode 100755
index 0000000..2f4dfb6
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/AuthServiceProvider.php
@@ -0,0 +1,112 @@
+registerAuthenticator();
+ $this->registerUserResolver();
+ $this->registerAccessGate();
+ $this->registerRequirePassword();
+ $this->registerRequestRebindHandler();
+ $this->registerEventRebindHandler();
+ }
+
+ /**
+ * Register the authenticator services.
+ *
+ * @return void
+ */
+ protected function registerAuthenticator()
+ {
+ $this->app->singleton('auth', fn ($app) => new AuthManager($app));
+
+ $this->app->singleton('auth.driver', fn ($app) => $app['auth']->guard());
+ }
+
+ /**
+ * Register a resolver for the authenticated user.
+ *
+ * @return void
+ */
+ protected function registerUserResolver()
+ {
+ $this->app->bind(AuthenticatableContract::class, fn ($app) => call_user_func($app['auth']->userResolver()));
+ }
+
+ /**
+ * Register the access gate service.
+ *
+ * @return void
+ */
+ protected function registerAccessGate()
+ {
+ $this->app->singleton(GateContract::class, function ($app) {
+ return new Gate($app, fn () => call_user_func($app['auth']->userResolver()));
+ });
+ }
+
+ /**
+ * Register a resolver for the authenticated user.
+ *
+ * @return void
+ */
+ protected function registerRequirePassword()
+ {
+ $this->app->bind(RequirePassword::class, function ($app) {
+ return new RequirePassword(
+ $app[ResponseFactory::class],
+ $app[UrlGenerator::class],
+ $app['config']->get('auth.password_timeout')
+ );
+ });
+ }
+
+ /**
+ * Handle the re-binding of the request binding.
+ *
+ * @return void
+ */
+ protected function registerRequestRebindHandler()
+ {
+ $this->app->rebinding('request', function ($app, $request) {
+ $request->setUserResolver(function ($guard = null) use ($app) {
+ return call_user_func($app['auth']->userResolver(), $guard);
+ });
+ });
+ }
+
+ /**
+ * Handle the re-binding of the event dispatcher binding.
+ *
+ * @return void
+ */
+ protected function registerEventRebindHandler()
+ {
+ $this->app->rebinding('events', function ($app, $dispatcher) {
+ if (! $app->resolved('auth') ||
+ $app['auth']->hasResolvedGuards() === false) {
+ return;
+ }
+
+ if (method_exists($guard = $app['auth']->guard(), 'setDispatcher')) {
+ $guard->setDispatcher($dispatcher);
+ }
+ });
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php b/vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php
new file mode 100644
index 0000000..f1c0115
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Authenticatable.php
@@ -0,0 +1,88 @@
+getKeyName();
+ }
+
+ /**
+ * Get the unique identifier for the user.
+ *
+ * @return mixed
+ */
+ public function getAuthIdentifier()
+ {
+ return $this->{$this->getAuthIdentifierName()};
+ }
+
+ /**
+ * Get the unique broadcast identifier for the user.
+ *
+ * @return mixed
+ */
+ public function getAuthIdentifierForBroadcasting()
+ {
+ return $this->getAuthIdentifier();
+ }
+
+ /**
+ * Get the password for the user.
+ *
+ * @return string
+ */
+ public function getAuthPassword()
+ {
+ return $this->password;
+ }
+
+ /**
+ * Get the token value for the "remember me" session.
+ *
+ * @return string|null
+ */
+ public function getRememberToken()
+ {
+ if (! empty($this->getRememberTokenName())) {
+ return (string) $this->{$this->getRememberTokenName()};
+ }
+ }
+
+ /**
+ * Set the token value for the "remember me" session.
+ *
+ * @param string $value
+ * @return void
+ */
+ public function setRememberToken($value)
+ {
+ if (! empty($this->getRememberTokenName())) {
+ $this->{$this->getRememberTokenName()} = $value;
+ }
+ }
+
+ /**
+ * Get the column name for the "remember me" token.
+ *
+ * @return string
+ */
+ public function getRememberTokenName()
+ {
+ return $this->rememberTokenName;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/AuthenticationException.php b/vendor/laravel/framework/src/Illuminate/Auth/AuthenticationException.php
new file mode 100644
index 0000000..66808c3
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/AuthenticationException.php
@@ -0,0 +1,58 @@
+guards = $guards;
+ $this->redirectTo = $redirectTo;
+ }
+
+ /**
+ * Get the guards that were checked.
+ *
+ * @return array
+ */
+ public function guards()
+ {
+ return $this->guards;
+ }
+
+ /**
+ * Get the path the user should be redirected to.
+ *
+ * @return string|null
+ */
+ public function redirectTo()
+ {
+ return $this->redirectTo;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Console/ClearResetsCommand.php b/vendor/laravel/framework/src/Illuminate/Auth/Console/ClearResetsCommand.php
new file mode 100644
index 0000000..9c3d9e0
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Console/ClearResetsCommand.php
@@ -0,0 +1,47 @@
+laravel['auth.password']->broker($this->argument('name'))->getRepository()->deleteExpired();
+
+ $this->info('Expired reset tokens cleared successfully.');
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/layouts/app.stub b/vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/layouts/app.stub
new file mode 100644
index 0000000..2f2986d
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Console/stubs/make/views/layouts/app.stub
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+ {{ config('app.name', 'Laravel') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ @yield('content')
+
+
+
+
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/CreatesUserProviders.php b/vendor/laravel/framework/src/Illuminate/Auth/CreatesUserProviders.php
new file mode 100644
index 0000000..761a427
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/CreatesUserProviders.php
@@ -0,0 +1,91 @@
+getProviderConfiguration($provider))) {
+ return;
+ }
+
+ if (isset($this->customProviderCreators[$driver = ($config['driver'] ?? null)])) {
+ return call_user_func(
+ $this->customProviderCreators[$driver], $this->app, $config
+ );
+ }
+
+ return match ($driver) {
+ 'database' => $this->createDatabaseProvider($config),
+ 'eloquent' => $this->createEloquentProvider($config),
+ default => throw new InvalidArgumentException(
+ "Authentication user provider [{$driver}] is not defined."
+ ),
+ };
+ }
+
+ /**
+ * Get the user provider configuration.
+ *
+ * @param string|null $provider
+ * @return array|null
+ */
+ protected function getProviderConfiguration($provider)
+ {
+ if ($provider = $provider ?: $this->getDefaultUserProvider()) {
+ return $this->app['config']['auth.providers.'.$provider];
+ }
+ }
+
+ /**
+ * Create an instance of the database user provider.
+ *
+ * @param array $config
+ * @return \Illuminate\Auth\DatabaseUserProvider
+ */
+ protected function createDatabaseProvider($config)
+ {
+ $connection = $this->app['db']->connection($config['connection'] ?? null);
+
+ return new DatabaseUserProvider($connection, $this->app['hash'], $config['table']);
+ }
+
+ /**
+ * Create an instance of the Eloquent user provider.
+ *
+ * @param array $config
+ * @return \Illuminate\Auth\EloquentUserProvider
+ */
+ protected function createEloquentProvider($config)
+ {
+ return new EloquentUserProvider($this->app['hash'], $config['model']);
+ }
+
+ /**
+ * Get the default user provider name.
+ *
+ * @return string
+ */
+ public function getDefaultUserProvider()
+ {
+ return $this->app['config']['auth.defaults.provider'];
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/DatabaseUserProvider.php b/vendor/laravel/framework/src/Illuminate/Auth/DatabaseUserProvider.php
new file mode 100755
index 0000000..16b70ee
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/DatabaseUserProvider.php
@@ -0,0 +1,161 @@
+connection = $connection;
+ $this->table = $table;
+ $this->hasher = $hasher;
+ }
+
+ /**
+ * Retrieve a user by their unique identifier.
+ *
+ * @param mixed $identifier
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function retrieveById($identifier)
+ {
+ $user = $this->connection->table($this->table)->find($identifier);
+
+ return $this->getGenericUser($user);
+ }
+
+ /**
+ * Retrieve a user by their unique identifier and "remember me" token.
+ *
+ * @param mixed $identifier
+ * @param string $token
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function retrieveByToken($identifier, $token)
+ {
+ $user = $this->getGenericUser(
+ $this->connection->table($this->table)->find($identifier)
+ );
+
+ return $user && $user->getRememberToken() && hash_equals($user->getRememberToken(), $token)
+ ? $user : null;
+ }
+
+ /**
+ * Update the "remember me" token for the given user in storage.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param string $token
+ * @return void
+ */
+ public function updateRememberToken(UserContract $user, $token)
+ {
+ $this->connection->table($this->table)
+ ->where($user->getAuthIdentifierName(), $user->getAuthIdentifier())
+ ->update([$user->getRememberTokenName() => $token]);
+ }
+
+ /**
+ * Retrieve a user by the given credentials.
+ *
+ * @param array $credentials
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function retrieveByCredentials(array $credentials)
+ {
+ $credentials = array_filter(
+ $credentials,
+ fn ($key) => ! str_contains($key, 'password'),
+ ARRAY_FILTER_USE_KEY
+ );
+
+ if (empty($credentials)) {
+ return;
+ }
+
+ // First we will add each credential element to the query as a where clause.
+ // Then we can execute the query and, if we found a user, return it in a
+ // generic "user" object that will be utilized by the Guard instances.
+ $query = $this->connection->table($this->table);
+
+ foreach ($credentials as $key => $value) {
+ if (is_array($value) || $value instanceof Arrayable) {
+ $query->whereIn($key, $value);
+ } elseif ($value instanceof Closure) {
+ $value($query);
+ } else {
+ $query->where($key, $value);
+ }
+ }
+
+ // Now we are ready to execute the query to see if we have a user matching
+ // the given credentials. If not, we will just return null and indicate
+ // that there are no matching users from the given credential arrays.
+ $user = $query->first();
+
+ return $this->getGenericUser($user);
+ }
+
+ /**
+ * Get the generic user.
+ *
+ * @param mixed $user
+ * @return \Illuminate\Auth\GenericUser|null
+ */
+ protected function getGenericUser($user)
+ {
+ if (! is_null($user)) {
+ return new GenericUser((array) $user);
+ }
+ }
+
+ /**
+ * Validate a user against the given credentials.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param array $credentials
+ * @return bool
+ */
+ public function validateCredentials(UserContract $user, array $credentials)
+ {
+ return $this->hasher->check(
+ $credentials['password'], $user->getAuthPassword()
+ );
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php b/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php
new file mode 100755
index 0000000..44f5898
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php
@@ -0,0 +1,220 @@
+model = $model;
+ $this->hasher = $hasher;
+ }
+
+ /**
+ * Retrieve a user by their unique identifier.
+ *
+ * @param mixed $identifier
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function retrieveById($identifier)
+ {
+ $model = $this->createModel();
+
+ return $this->newModelQuery($model)
+ ->where($model->getAuthIdentifierName(), $identifier)
+ ->first();
+ }
+
+ /**
+ * Retrieve a user by their unique identifier and "remember me" token.
+ *
+ * @param mixed $identifier
+ * @param string $token
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function retrieveByToken($identifier, $token)
+ {
+ $model = $this->createModel();
+
+ $retrievedModel = $this->newModelQuery($model)->where(
+ $model->getAuthIdentifierName(), $identifier
+ )->first();
+
+ if (! $retrievedModel) {
+ return;
+ }
+
+ $rememberToken = $retrievedModel->getRememberToken();
+
+ return $rememberToken && hash_equals($rememberToken, $token)
+ ? $retrievedModel : null;
+ }
+
+ /**
+ * Update the "remember me" token for the given user in storage.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param string $token
+ * @return void
+ */
+ public function updateRememberToken(UserContract $user, $token)
+ {
+ $user->setRememberToken($token);
+
+ $timestamps = $user->timestamps;
+
+ $user->timestamps = false;
+
+ $user->save();
+
+ $user->timestamps = $timestamps;
+ }
+
+ /**
+ * Retrieve a user by the given credentials.
+ *
+ * @param array $credentials
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function retrieveByCredentials(array $credentials)
+ {
+ $credentials = array_filter(
+ $credentials,
+ fn ($key) => ! str_contains($key, 'password'),
+ ARRAY_FILTER_USE_KEY
+ );
+
+ if (empty($credentials)) {
+ return;
+ }
+
+ // First we will add each credential element to the query as a where clause.
+ // Then we can execute the query and, if we found a user, return it in a
+ // Eloquent User "model" that will be utilized by the Guard instances.
+ $query = $this->newModelQuery();
+
+ foreach ($credentials as $key => $value) {
+ if (is_array($value) || $value instanceof Arrayable) {
+ $query->whereIn($key, $value);
+ } elseif ($value instanceof Closure) {
+ $value($query);
+ } else {
+ $query->where($key, $value);
+ }
+ }
+
+ return $query->first();
+ }
+
+ /**
+ * Validate a user against the given credentials.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param array $credentials
+ * @return bool
+ */
+ public function validateCredentials(UserContract $user, array $credentials)
+ {
+ $plain = $credentials['password'];
+
+ return $this->hasher->check($plain, $user->getAuthPassword());
+ }
+
+ /**
+ * Get a new query builder for the model instance.
+ *
+ * @param \Illuminate\Database\Eloquent\Model|null $model
+ * @return \Illuminate\Database\Eloquent\Builder
+ */
+ protected function newModelQuery($model = null)
+ {
+ return is_null($model)
+ ? $this->createModel()->newQuery()
+ : $model->newQuery();
+ }
+
+ /**
+ * Create a new instance of the model.
+ *
+ * @return \Illuminate\Database\Eloquent\Model
+ */
+ public function createModel()
+ {
+ $class = '\\'.ltrim($this->model, '\\');
+
+ return new $class;
+ }
+
+ /**
+ * Gets the hasher implementation.
+ *
+ * @return \Illuminate\Contracts\Hashing\Hasher
+ */
+ public function getHasher()
+ {
+ return $this->hasher;
+ }
+
+ /**
+ * Sets the hasher implementation.
+ *
+ * @param \Illuminate\Contracts\Hashing\Hasher $hasher
+ * @return $this
+ */
+ public function setHasher(HasherContract $hasher)
+ {
+ $this->hasher = $hasher;
+
+ return $this;
+ }
+
+ /**
+ * Gets the name of the Eloquent user model.
+ *
+ * @return string
+ */
+ public function getModel()
+ {
+ return $this->model;
+ }
+
+ /**
+ * Sets the name of the Eloquent user model.
+ *
+ * @param string $model
+ * @return $this
+ */
+ public function setModel($model)
+ {
+ $this->model = $model;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/Attempting.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/Attempting.php
new file mode 100644
index 0000000..3f911ba
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/Attempting.php
@@ -0,0 +1,42 @@
+guard = $guard;
+ $this->remember = $remember;
+ $this->credentials = $credentials;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/Authenticated.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/Authenticated.php
new file mode 100644
index 0000000..faefcbe
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/Authenticated.php
@@ -0,0 +1,37 @@
+user = $user;
+ $this->guard = $guard;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/CurrentDeviceLogout.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/CurrentDeviceLogout.php
new file mode 100644
index 0000000..32d31fa
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/CurrentDeviceLogout.php
@@ -0,0 +1,37 @@
+user = $user;
+ $this->guard = $guard;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/Failed.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/Failed.php
new file mode 100644
index 0000000..34f8124
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/Failed.php
@@ -0,0 +1,42 @@
+user = $user;
+ $this->guard = $guard;
+ $this->credentials = $credentials;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/Lockout.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/Lockout.php
new file mode 100644
index 0000000..347943f
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/Lockout.php
@@ -0,0 +1,26 @@
+request = $request;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/Login.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/Login.php
new file mode 100644
index 0000000..87a399e
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/Login.php
@@ -0,0 +1,46 @@
+user = $user;
+ $this->guard = $guard;
+ $this->remember = $remember;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/Logout.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/Logout.php
new file mode 100644
index 0000000..c47341d
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/Logout.php
@@ -0,0 +1,37 @@
+user = $user;
+ $this->guard = $guard;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/OtherDeviceLogout.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/OtherDeviceLogout.php
new file mode 100644
index 0000000..ea139a7
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/OtherDeviceLogout.php
@@ -0,0 +1,37 @@
+user = $user;
+ $this->guard = $guard;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/PasswordReset.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/PasswordReset.php
new file mode 100644
index 0000000..f57b3c9
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/PasswordReset.php
@@ -0,0 +1,28 @@
+user = $user;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/Registered.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/Registered.php
new file mode 100644
index 0000000..f84058c
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/Registered.php
@@ -0,0 +1,28 @@
+user = $user;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/Validated.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/Validated.php
new file mode 100644
index 0000000..ebc3b2c
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/Validated.php
@@ -0,0 +1,37 @@
+user = $user;
+ $this->guard = $guard;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Events/Verified.php b/vendor/laravel/framework/src/Illuminate/Auth/Events/Verified.php
new file mode 100644
index 0000000..1d6e4c0
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Events/Verified.php
@@ -0,0 +1,28 @@
+user = $user;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/GenericUser.php b/vendor/laravel/framework/src/Illuminate/Auth/GenericUser.php
new file mode 100755
index 0000000..c87bc23
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/GenericUser.php
@@ -0,0 +1,132 @@
+attributes = $attributes;
+ }
+
+ /**
+ * Get the name of the unique identifier for the user.
+ *
+ * @return string
+ */
+ public function getAuthIdentifierName()
+ {
+ return 'id';
+ }
+
+ /**
+ * Get the unique identifier for the user.
+ *
+ * @return mixed
+ */
+ public function getAuthIdentifier()
+ {
+ return $this->attributes[$this->getAuthIdentifierName()];
+ }
+
+ /**
+ * Get the password for the user.
+ *
+ * @return string
+ */
+ public function getAuthPassword()
+ {
+ return $this->attributes['password'];
+ }
+
+ /**
+ * Get the "remember me" token value.
+ *
+ * @return string
+ */
+ public function getRememberToken()
+ {
+ return $this->attributes[$this->getRememberTokenName()];
+ }
+
+ /**
+ * Set the "remember me" token value.
+ *
+ * @param string $value
+ * @return void
+ */
+ public function setRememberToken($value)
+ {
+ $this->attributes[$this->getRememberTokenName()] = $value;
+ }
+
+ /**
+ * Get the column name for the "remember me" token.
+ *
+ * @return string
+ */
+ public function getRememberTokenName()
+ {
+ return 'remember_token';
+ }
+
+ /**
+ * Dynamically access the user's attributes.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ return $this->attributes[$key];
+ }
+
+ /**
+ * Dynamically set an attribute on the user.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return void
+ */
+ public function __set($key, $value)
+ {
+ $this->attributes[$key] = $value;
+ }
+
+ /**
+ * Dynamically check if a value is set on the user.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function __isset($key)
+ {
+ return isset($this->attributes[$key]);
+ }
+
+ /**
+ * Dynamically unset a value on the user.
+ *
+ * @param string $key
+ * @return void
+ */
+ public function __unset($key)
+ {
+ unset($this->attributes[$key]);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/GuardHelpers.php b/vendor/laravel/framework/src/Illuminate/Auth/GuardHelpers.php
new file mode 100644
index 0000000..aa9ebf9
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/GuardHelpers.php
@@ -0,0 +1,118 @@
+user())) {
+ return $user;
+ }
+
+ throw new AuthenticationException;
+ }
+
+ /**
+ * Determine if the guard has a user instance.
+ *
+ * @return bool
+ */
+ public function hasUser()
+ {
+ return ! is_null($this->user);
+ }
+
+ /**
+ * Determine if the current user is authenticated.
+ *
+ * @return bool
+ */
+ public function check()
+ {
+ return ! is_null($this->user());
+ }
+
+ /**
+ * Determine if the current user is a guest.
+ *
+ * @return bool
+ */
+ public function guest()
+ {
+ return ! $this->check();
+ }
+
+ /**
+ * Get the ID for the currently authenticated user.
+ *
+ * @return int|string|null
+ */
+ public function id()
+ {
+ if ($this->user()) {
+ return $this->user()->getAuthIdentifier();
+ }
+ }
+
+ /**
+ * Set the current user.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @return $this
+ */
+ public function setUser(AuthenticatableContract $user)
+ {
+ $this->user = $user;
+
+ return $this;
+ }
+
+ /**
+ * Get the user provider used by the guard.
+ *
+ * @return \Illuminate\Contracts\Auth\UserProvider
+ */
+ public function getProvider()
+ {
+ return $this->provider;
+ }
+
+ /**
+ * Set the user provider used by the guard.
+ *
+ * @param \Illuminate\Contracts\Auth\UserProvider $provider
+ * @return void
+ */
+ public function setProvider(UserProvider $provider)
+ {
+ $this->provider = $provider;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/LICENSE.md b/vendor/laravel/framework/src/Illuminate/Auth/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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.
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Listeners/SendEmailVerificationNotification.php b/vendor/laravel/framework/src/Illuminate/Auth/Listeners/SendEmailVerificationNotification.php
new file mode 100644
index 0000000..12dfa69
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Listeners/SendEmailVerificationNotification.php
@@ -0,0 +1,22 @@
+user instanceof MustVerifyEmail && ! $event->user->hasVerifiedEmail()) {
+ $event->user->sendEmailVerificationNotification();
+ }
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php
new file mode 100644
index 0000000..7eda342
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php
@@ -0,0 +1,97 @@
+auth = $auth;
+ }
+
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @param string[] ...$guards
+ * @return mixed
+ *
+ * @throws \Illuminate\Auth\AuthenticationException
+ */
+ public function handle($request, Closure $next, ...$guards)
+ {
+ $this->authenticate($request, $guards);
+
+ return $next($request);
+ }
+
+ /**
+ * Determine if the user is logged in to any of the given guards.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param array $guards
+ * @return void
+ *
+ * @throws \Illuminate\Auth\AuthenticationException
+ */
+ protected function authenticate($request, array $guards)
+ {
+ if (empty($guards)) {
+ $guards = [null];
+ }
+
+ foreach ($guards as $guard) {
+ if ($this->auth->guard($guard)->check()) {
+ return $this->auth->shouldUse($guard);
+ }
+ }
+
+ $this->unauthenticated($request, $guards);
+ }
+
+ /**
+ * Handle an unauthenticated user.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param array $guards
+ * @return void
+ *
+ * @throws \Illuminate\Auth\AuthenticationException
+ */
+ protected function unauthenticated($request, array $guards)
+ {
+ throw new AuthenticationException(
+ 'Unauthenticated.', $guards, $this->redirectTo($request)
+ );
+ }
+
+ /**
+ * Get the path the user should be redirected to when they are not authenticated.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return string|null
+ */
+ protected function redirectTo($request)
+ {
+ //
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Middleware/AuthenticateWithBasicAuth.php b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/AuthenticateWithBasicAuth.php
new file mode 100644
index 0000000..92c81e6
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/AuthenticateWithBasicAuth.php
@@ -0,0 +1,45 @@
+auth = $auth;
+ }
+
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @param string|null $guard
+ * @param string|null $field
+ * @return mixed
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
+ */
+ public function handle($request, Closure $next, $guard = null, $field = null)
+ {
+ $this->auth->guard($guard)->basic($field ?: 'email');
+
+ return $next($request);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authorize.php b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authorize.php
new file mode 100644
index 0000000..1af05be
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authorize.php
@@ -0,0 +1,93 @@
+gate = $gate;
+ }
+
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @param string $ability
+ * @param array|null ...$models
+ * @return mixed
+ *
+ * @throws \Illuminate\Auth\AuthenticationException
+ * @throws \Illuminate\Auth\Access\AuthorizationException
+ */
+ public function handle($request, Closure $next, $ability, ...$models)
+ {
+ $this->gate->authorize($ability, $this->getGateArguments($request, $models));
+
+ return $next($request);
+ }
+
+ /**
+ * Get the arguments parameter for the gate.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param array|null $models
+ * @return \Illuminate\Database\Eloquent\Model|array|string
+ */
+ protected function getGateArguments($request, $models)
+ {
+ if (is_null($models)) {
+ return [];
+ }
+
+ return collect($models)->map(function ($model) use ($request) {
+ return $model instanceof Model ? $model : $this->getModel($request, $model);
+ })->all();
+ }
+
+ /**
+ * Get the model to authorize.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param string $model
+ * @return \Illuminate\Database\Eloquent\Model|string
+ */
+ protected function getModel($request, $model)
+ {
+ if ($this->isClassName($model)) {
+ return trim($model);
+ } else {
+ return $request->route($model, null) ??
+ ((preg_match("/^['\"](.*)['\"]$/", trim($model), $matches)) ? $matches[1] : null);
+ }
+ }
+
+ /**
+ * Checks if the given string looks like a fully qualified class name.
+ *
+ * @param string $value
+ * @return bool
+ */
+ protected function isClassName($value)
+ {
+ return str_contains($value, '\\');
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Middleware/EnsureEmailIsVerified.php b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/EnsureEmailIsVerified.php
new file mode 100644
index 0000000..8f2b33a
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/EnsureEmailIsVerified.php
@@ -0,0 +1,32 @@
+user() ||
+ ($request->user() instanceof MustVerifyEmail &&
+ ! $request->user()->hasVerifiedEmail())) {
+ return $request->expectsJson()
+ ? abort(403, 'Your email address is not verified.')
+ : Redirect::guest(URL::route($redirectToRoute ?: 'verification.notice'));
+ }
+
+ return $next($request);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Middleware/RequirePassword.php b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/RequirePassword.php
new file mode 100644
index 0000000..4ed4395
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Middleware/RequirePassword.php
@@ -0,0 +1,86 @@
+responseFactory = $responseFactory;
+ $this->urlGenerator = $urlGenerator;
+ $this->passwordTimeout = $passwordTimeout ?: 10800;
+ }
+
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @param string|null $redirectToRoute
+ * @param int|null $passwordTimeoutSeconds
+ * @return mixed
+ */
+ public function handle($request, Closure $next, $redirectToRoute = null, $passwordTimeoutSeconds = null)
+ {
+ if ($this->shouldConfirmPassword($request, $passwordTimeoutSeconds)) {
+ if ($request->expectsJson()) {
+ return $this->responseFactory->json([
+ 'message' => 'Password confirmation required.',
+ ], 423);
+ }
+
+ return $this->responseFactory->redirectGuest(
+ $this->urlGenerator->route($redirectToRoute ?? 'password.confirm')
+ );
+ }
+
+ return $next($request);
+ }
+
+ /**
+ * Determine if the confirmation timeout has expired.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param int|null $passwordTimeoutSeconds
+ * @return bool
+ */
+ protected function shouldConfirmPassword($request, $passwordTimeoutSeconds = null)
+ {
+ $confirmedAt = time() - $request->session()->get('auth.password_confirmed_at', 0);
+
+ return $confirmedAt > ($passwordTimeoutSeconds ?? $this->passwordTimeout);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/MustVerifyEmail.php b/vendor/laravel/framework/src/Illuminate/Auth/MustVerifyEmail.php
new file mode 100644
index 0000000..8e1ce33
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/MustVerifyEmail.php
@@ -0,0 +1,50 @@
+email_verified_at);
+ }
+
+ /**
+ * Mark the given user's email as verified.
+ *
+ * @return bool
+ */
+ public function markEmailAsVerified()
+ {
+ return $this->forceFill([
+ 'email_verified_at' => $this->freshTimestamp(),
+ ])->save();
+ }
+
+ /**
+ * Send the email verification notification.
+ *
+ * @return void
+ */
+ public function sendEmailVerificationNotification()
+ {
+ $this->notify(new VerifyEmail);
+ }
+
+ /**
+ * Get the email address that should be used for verification.
+ *
+ * @return string
+ */
+ public function getEmailForVerification()
+ {
+ return $this->email;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php b/vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php
new file mode 100644
index 0000000..1d8da41
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php
@@ -0,0 +1,124 @@
+token = $token;
+ }
+
+ /**
+ * Get the notification's channels.
+ *
+ * @param mixed $notifiable
+ * @return array|string
+ */
+ public function via($notifiable)
+ {
+ return ['mail'];
+ }
+
+ /**
+ * Build the mail representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ public function toMail($notifiable)
+ {
+ if (static::$toMailCallback) {
+ return call_user_func(static::$toMailCallback, $notifiable, $this->token);
+ }
+
+ return $this->buildMailMessage($this->resetUrl($notifiable));
+ }
+
+ /**
+ * Get the reset password notification mail message for the given URL.
+ *
+ * @param string $url
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ protected function buildMailMessage($url)
+ {
+ return (new MailMessage)
+ ->subject(Lang::get('Reset Password Notification'))
+ ->line(Lang::get('You are receiving this email because we received a password reset request for your account.'))
+ ->action(Lang::get('Reset Password'), $url)
+ ->line(Lang::get('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')]))
+ ->line(Lang::get('If you did not request a password reset, no further action is required.'));
+ }
+
+ /**
+ * Get the reset URL for the given notifiable.
+ *
+ * @param mixed $notifiable
+ * @return string
+ */
+ protected function resetUrl($notifiable)
+ {
+ if (static::$createUrlCallback) {
+ return call_user_func(static::$createUrlCallback, $notifiable, $this->token);
+ }
+
+ return url(route('password.reset', [
+ 'token' => $this->token,
+ 'email' => $notifiable->getEmailForPasswordReset(),
+ ], false));
+ }
+
+ /**
+ * Set a callback that should be used when creating the reset password button URL.
+ *
+ * @param \Closure(mixed, string): string $callback
+ * @return void
+ */
+ public static function createUrlUsing($callback)
+ {
+ static::$createUrlCallback = $callback;
+ }
+
+ /**
+ * Set a callback that should be used when building the notification mail message.
+ *
+ * @param \Closure(mixed, string): \Illuminate\Notifications\Messages\MailMessage $callback
+ * @return void
+ */
+ public static function toMailUsing($callback)
+ {
+ static::$toMailCallback = $callback;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Notifications/VerifyEmail.php b/vendor/laravel/framework/src/Illuminate/Auth/Notifications/VerifyEmail.php
new file mode 100644
index 0000000..7a5cf91
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Notifications/VerifyEmail.php
@@ -0,0 +1,114 @@
+verificationUrl($notifiable);
+
+ if (static::$toMailCallback) {
+ return call_user_func(static::$toMailCallback, $notifiable, $verificationUrl);
+ }
+
+ return $this->buildMailMessage($verificationUrl);
+ }
+
+ /**
+ * Get the verify email notification mail message for the given URL.
+ *
+ * @param string $url
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ protected function buildMailMessage($url)
+ {
+ return (new MailMessage)
+ ->subject(Lang::get('Verify Email Address'))
+ ->line(Lang::get('Please click the button below to verify your email address.'))
+ ->action(Lang::get('Verify Email Address'), $url)
+ ->line(Lang::get('If you did not create an account, no further action is required.'));
+ }
+
+ /**
+ * Get the verification URL for the given notifiable.
+ *
+ * @param mixed $notifiable
+ * @return string
+ */
+ protected function verificationUrl($notifiable)
+ {
+ if (static::$createUrlCallback) {
+ return call_user_func(static::$createUrlCallback, $notifiable);
+ }
+
+ return URL::temporarySignedRoute(
+ 'verification.verify',
+ Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
+ [
+ 'id' => $notifiable->getKey(),
+ 'hash' => sha1($notifiable->getEmailForVerification()),
+ ]
+ );
+ }
+
+ /**
+ * Set a callback that should be used when creating the email verification URL.
+ *
+ * @param \Closure $callback
+ * @return void
+ */
+ public static function createUrlUsing($callback)
+ {
+ static::$createUrlCallback = $callback;
+ }
+
+ /**
+ * Set a callback that should be used when building the notification mail message.
+ *
+ * @param \Closure $callback
+ * @return void
+ */
+ public static function toMailUsing($callback)
+ {
+ static::$toMailCallback = $callback;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Passwords/CanResetPassword.php b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/CanResetPassword.php
new file mode 100644
index 0000000..918a288
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/CanResetPassword.php
@@ -0,0 +1,29 @@
+email;
+ }
+
+ /**
+ * Send the password reset notification.
+ *
+ * @param string $token
+ * @return void
+ */
+ public function sendPasswordResetNotification($token)
+ {
+ $this->notify(new ResetPasswordNotification($token));
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php
new file mode 100755
index 0000000..fe5f54b
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php
@@ -0,0 +1,246 @@
+table = $table;
+ $this->hasher = $hasher;
+ $this->hashKey = $hashKey;
+ $this->expires = $expires * 60;
+ $this->connection = $connection;
+ $this->throttle = $throttle;
+ }
+
+ /**
+ * Create a new token record.
+ *
+ * @param \Illuminate\Contracts\Auth\CanResetPassword $user
+ * @return string
+ */
+ public function create(CanResetPasswordContract $user)
+ {
+ $email = $user->getEmailForPasswordReset();
+
+ $this->deleteExisting($user);
+
+ // We will create a new, random token for the user so that we can e-mail them
+ // a safe link to the password reset form. Then we will insert a record in
+ // the database so that we can verify the token within the actual reset.
+ $token = $this->createNewToken();
+
+ $this->getTable()->insert($this->getPayload($email, $token));
+
+ return $token;
+ }
+
+ /**
+ * Delete all existing reset tokens from the database.
+ *
+ * @param \Illuminate\Contracts\Auth\CanResetPassword $user
+ * @return int
+ */
+ protected function deleteExisting(CanResetPasswordContract $user)
+ {
+ return $this->getTable()->where('email', $user->getEmailForPasswordReset())->delete();
+ }
+
+ /**
+ * Build the record payload for the table.
+ *
+ * @param string $email
+ * @param string $token
+ * @return array
+ */
+ protected function getPayload($email, $token)
+ {
+ return ['email' => $email, 'token' => $this->hasher->make($token), 'created_at' => new Carbon];
+ }
+
+ /**
+ * Determine if a token record exists and is valid.
+ *
+ * @param \Illuminate\Contracts\Auth\CanResetPassword $user
+ * @param string $token
+ * @return bool
+ */
+ public function exists(CanResetPasswordContract $user, $token)
+ {
+ $record = (array) $this->getTable()->where(
+ 'email', $user->getEmailForPasswordReset()
+ )->first();
+
+ return $record &&
+ ! $this->tokenExpired($record['created_at']) &&
+ $this->hasher->check($token, $record['token']);
+ }
+
+ /**
+ * Determine if the token has expired.
+ *
+ * @param string $createdAt
+ * @return bool
+ */
+ protected function tokenExpired($createdAt)
+ {
+ return Carbon::parse($createdAt)->addSeconds($this->expires)->isPast();
+ }
+
+ /**
+ * Determine if the given user recently created a password reset token.
+ *
+ * @param \Illuminate\Contracts\Auth\CanResetPassword $user
+ * @return bool
+ */
+ public function recentlyCreatedToken(CanResetPasswordContract $user)
+ {
+ $record = (array) $this->getTable()->where(
+ 'email', $user->getEmailForPasswordReset()
+ )->first();
+
+ return $record && $this->tokenRecentlyCreated($record['created_at']);
+ }
+
+ /**
+ * Determine if the token was recently created.
+ *
+ * @param string $createdAt
+ * @return bool
+ */
+ protected function tokenRecentlyCreated($createdAt)
+ {
+ if ($this->throttle <= 0) {
+ return false;
+ }
+
+ return Carbon::parse($createdAt)->addSeconds(
+ $this->throttle
+ )->isFuture();
+ }
+
+ /**
+ * Delete a token record by user.
+ *
+ * @param \Illuminate\Contracts\Auth\CanResetPassword $user
+ * @return void
+ */
+ public function delete(CanResetPasswordContract $user)
+ {
+ $this->deleteExisting($user);
+ }
+
+ /**
+ * Delete expired tokens.
+ *
+ * @return void
+ */
+ public function deleteExpired()
+ {
+ $expiredAt = Carbon::now()->subSeconds($this->expires);
+
+ $this->getTable()->where('created_at', '<', $expiredAt)->delete();
+ }
+
+ /**
+ * Create a new token for the user.
+ *
+ * @return string
+ */
+ public function createNewToken()
+ {
+ return hash_hmac('sha256', Str::random(40), $this->hashKey);
+ }
+
+ /**
+ * Get the database connection instance.
+ *
+ * @return \Illuminate\Database\ConnectionInterface
+ */
+ public function getConnection()
+ {
+ return $this->connection;
+ }
+
+ /**
+ * Begin a new database query against the table.
+ *
+ * @return \Illuminate\Database\Query\Builder
+ */
+ protected function getTable()
+ {
+ return $this->connection->table($this->table);
+ }
+
+ /**
+ * Get the hasher instance.
+ *
+ * @return \Illuminate\Contracts\Hashing\Hasher
+ */
+ public function getHasher()
+ {
+ return $this->hasher;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordBroker.php b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordBroker.php
new file mode 100755
index 0000000..cbbc897
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordBroker.php
@@ -0,0 +1,190 @@
+users = $users;
+ $this->tokens = $tokens;
+ }
+
+ /**
+ * Send a password reset link to a user.
+ *
+ * @param array $credentials
+ * @param \Closure|null $callback
+ * @return string
+ */
+ public function sendResetLink(array $credentials, Closure $callback = null)
+ {
+ // First we will check to see if we found a user at the given credentials and
+ // if we did not we will redirect back to this current URI with a piece of
+ // "flash" data in the session to indicate to the developers the errors.
+ $user = $this->getUser($credentials);
+
+ if (is_null($user)) {
+ return static::INVALID_USER;
+ }
+
+ if ($this->tokens->recentlyCreatedToken($user)) {
+ return static::RESET_THROTTLED;
+ }
+
+ $token = $this->tokens->create($user);
+
+ if ($callback) {
+ $callback($user, $token);
+ } else {
+ // Once we have the reset token, we are ready to send the message out to this
+ // user with a link to reset their password. We will then redirect back to
+ // the current URI having nothing set in the session to indicate errors.
+ $user->sendPasswordResetNotification($token);
+ }
+
+ return static::RESET_LINK_SENT;
+ }
+
+ /**
+ * Reset the password for the given token.
+ *
+ * @param array $credentials
+ * @param \Closure $callback
+ * @return mixed
+ */
+ public function reset(array $credentials, Closure $callback)
+ {
+ $user = $this->validateReset($credentials);
+
+ // If the responses from the validate method is not a user instance, we will
+ // assume that it is a redirect and simply return it from this method and
+ // the user is properly redirected having an error message on the post.
+ if (! $user instanceof CanResetPasswordContract) {
+ return $user;
+ }
+
+ $password = $credentials['password'];
+
+ // Once the reset has been validated, we'll call the given callback with the
+ // new password. This gives the user an opportunity to store the password
+ // in their persistent storage. Then we'll delete the token and return.
+ $callback($user, $password);
+
+ $this->tokens->delete($user);
+
+ return static::PASSWORD_RESET;
+ }
+
+ /**
+ * Validate a password reset for the given credentials.
+ *
+ * @param array $credentials
+ * @return \Illuminate\Contracts\Auth\CanResetPassword|string
+ */
+ protected function validateReset(array $credentials)
+ {
+ if (is_null($user = $this->getUser($credentials))) {
+ return static::INVALID_USER;
+ }
+
+ if (! $this->tokens->exists($user, $credentials['token'])) {
+ return static::INVALID_TOKEN;
+ }
+
+ return $user;
+ }
+
+ /**
+ * Get the user for the given credentials.
+ *
+ * @param array $credentials
+ * @return \Illuminate\Contracts\Auth\CanResetPassword|null
+ *
+ * @throws \UnexpectedValueException
+ */
+ public function getUser(array $credentials)
+ {
+ $credentials = Arr::except($credentials, ['token']);
+
+ $user = $this->users->retrieveByCredentials($credentials);
+
+ if ($user && ! $user instanceof CanResetPasswordContract) {
+ throw new UnexpectedValueException('User must implement CanResetPassword interface.');
+ }
+
+ return $user;
+ }
+
+ /**
+ * Create a new password reset token for the given user.
+ *
+ * @param \Illuminate\Contracts\Auth\CanResetPassword $user
+ * @return string
+ */
+ public function createToken(CanResetPasswordContract $user)
+ {
+ return $this->tokens->create($user);
+ }
+
+ /**
+ * Delete password reset tokens of the given user.
+ *
+ * @param \Illuminate\Contracts\Auth\CanResetPassword $user
+ * @return void
+ */
+ public function deleteToken(CanResetPasswordContract $user)
+ {
+ $this->tokens->delete($user);
+ }
+
+ /**
+ * Validate the given password reset token.
+ *
+ * @param \Illuminate\Contracts\Auth\CanResetPassword $user
+ * @param string $token
+ * @return bool
+ */
+ public function tokenExists(CanResetPasswordContract $user, $token)
+ {
+ return $this->tokens->exists($user, $token);
+ }
+
+ /**
+ * Get the password reset token repository implementation.
+ *
+ * @return \Illuminate\Auth\Passwords\TokenRepositoryInterface
+ */
+ public function getRepository()
+ {
+ return $this->tokens;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php
new file mode 100644
index 0000000..01cffe8
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordBrokerManager.php
@@ -0,0 +1,145 @@
+app = $app;
+ }
+
+ /**
+ * Attempt to get the broker from the local cache.
+ *
+ * @param string|null $name
+ * @return \Illuminate\Contracts\Auth\PasswordBroker
+ */
+ public function broker($name = null)
+ {
+ $name = $name ?: $this->getDefaultDriver();
+
+ return $this->brokers[$name] ?? ($this->brokers[$name] = $this->resolve($name));
+ }
+
+ /**
+ * Resolve the given broker.
+ *
+ * @param string $name
+ * @return \Illuminate\Contracts\Auth\PasswordBroker
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function resolve($name)
+ {
+ $config = $this->getConfig($name);
+
+ if (is_null($config)) {
+ throw new InvalidArgumentException("Password resetter [{$name}] is not defined.");
+ }
+
+ // The password broker uses a token repository to validate tokens and send user
+ // password e-mails, as well as validating that password reset process as an
+ // aggregate service of sorts providing a convenient interface for resets.
+ return new PasswordBroker(
+ $this->createTokenRepository($config),
+ $this->app['auth']->createUserProvider($config['provider'] ?? null)
+ );
+ }
+
+ /**
+ * Create a token repository instance based on the given configuration.
+ *
+ * @param array $config
+ * @return \Illuminate\Auth\Passwords\TokenRepositoryInterface
+ */
+ protected function createTokenRepository(array $config)
+ {
+ $key = $this->app['config']['app.key'];
+
+ if (str_starts_with($key, 'base64:')) {
+ $key = base64_decode(substr($key, 7));
+ }
+
+ $connection = $config['connection'] ?? null;
+
+ return new DatabaseTokenRepository(
+ $this->app['db']->connection($connection),
+ $this->app['hash'],
+ $config['table'],
+ $key,
+ $config['expire'],
+ $config['throttle'] ?? 0
+ );
+ }
+
+ /**
+ * Get the password broker configuration.
+ *
+ * @param string $name
+ * @return array
+ */
+ protected function getConfig($name)
+ {
+ return $this->app['config']["auth.passwords.{$name}"];
+ }
+
+ /**
+ * Get the default password broker name.
+ *
+ * @return string
+ */
+ public function getDefaultDriver()
+ {
+ return $this->app['config']['auth.defaults.passwords'];
+ }
+
+ /**
+ * Set the default password broker name.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function setDefaultDriver($name)
+ {
+ $this->app['config']['auth.defaults.passwords'] = $name;
+ }
+
+ /**
+ * Dynamically call the default driver instance.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->broker()->{$method}(...$parameters);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordResetServiceProvider.php b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordResetServiceProvider.php
new file mode 100755
index 0000000..a26b7e6
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordResetServiceProvider.php
@@ -0,0 +1,45 @@
+registerPasswordBroker();
+ }
+
+ /**
+ * Register the password broker instance.
+ *
+ * @return void
+ */
+ protected function registerPasswordBroker()
+ {
+ $this->app->singleton('auth.password', function ($app) {
+ return new PasswordBrokerManager($app);
+ });
+
+ $this->app->bind('auth.password.broker', function ($app) {
+ return $app->make('auth.password')->broker();
+ });
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides()
+ {
+ return ['auth.password', 'auth.password.broker'];
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/Passwords/TokenRepositoryInterface.php b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/TokenRepositoryInterface.php
new file mode 100755
index 0000000..47c1758
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/Passwords/TokenRepositoryInterface.php
@@ -0,0 +1,48 @@
+recaller = @unserialize($recaller, ['allowed_classes' => false]) ?: $recaller;
+ }
+
+ /**
+ * Get the user ID from the recaller.
+ *
+ * @return string
+ */
+ public function id()
+ {
+ return explode('|', $this->recaller, 3)[0];
+ }
+
+ /**
+ * Get the "remember token" token from the recaller.
+ *
+ * @return string
+ */
+ public function token()
+ {
+ return explode('|', $this->recaller, 3)[1];
+ }
+
+ /**
+ * Get the password from the recaller.
+ *
+ * @return string
+ */
+ public function hash()
+ {
+ return explode('|', $this->recaller, 4)[2];
+ }
+
+ /**
+ * Determine if the recaller is valid.
+ *
+ * @return bool
+ */
+ public function valid()
+ {
+ return $this->properString() && $this->hasAllSegments();
+ }
+
+ /**
+ * Determine if the recaller is an invalid string.
+ *
+ * @return bool
+ */
+ protected function properString()
+ {
+ return is_string($this->recaller) && str_contains($this->recaller, '|');
+ }
+
+ /**
+ * Determine if the recaller has all segments.
+ *
+ * @return bool
+ */
+ protected function hasAllSegments()
+ {
+ $segments = explode('|', $this->recaller);
+
+ return count($segments) >= 3 && trim($segments[0]) !== '' && trim($segments[1]) !== '';
+ }
+
+ /**
+ * Get the recaller's segments.
+ *
+ * @return array
+ */
+ public function segments()
+ {
+ return explode('|', $this->recaller);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/RequestGuard.php b/vendor/laravel/framework/src/Illuminate/Auth/RequestGuard.php
new file mode 100644
index 0000000..d0af83c
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/RequestGuard.php
@@ -0,0 +1,87 @@
+request = $request;
+ $this->callback = $callback;
+ $this->provider = $provider;
+ }
+
+ /**
+ * Get the currently authenticated user.
+ *
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function user()
+ {
+ // If we've already retrieved the user for the current request we can just
+ // return it back immediately. We do not want to fetch the user data on
+ // every call to this method because that would be tremendously slow.
+ if (! is_null($this->user)) {
+ return $this->user;
+ }
+
+ return $this->user = call_user_func(
+ $this->callback, $this->request, $this->getProvider()
+ );
+ }
+
+ /**
+ * Validate a user's credentials.
+ *
+ * @param array $credentials
+ * @return bool
+ */
+ public function validate(array $credentials = [])
+ {
+ return ! is_null((new static(
+ $this->callback, $credentials['request'], $this->getProvider()
+ ))->user());
+ }
+
+ /**
+ * Set the current request instance.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return $this
+ */
+ public function setRequest(Request $request)
+ {
+ $this->request = $request;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php b/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
new file mode 100644
index 0000000..652835f
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
@@ -0,0 +1,934 @@
+name = $name;
+ $this->session = $session;
+ $this->request = $request;
+ $this->provider = $provider;
+ }
+
+ /**
+ * Get the currently authenticated user.
+ *
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function user()
+ {
+ if ($this->loggedOut) {
+ return;
+ }
+
+ // If we've already retrieved the user for the current request we can just
+ // return it back immediately. We do not want to fetch the user data on
+ // every call to this method because that would be tremendously slow.
+ if (! is_null($this->user)) {
+ return $this->user;
+ }
+
+ $id = $this->session->get($this->getName());
+
+ // First we will try to load the user using the identifier in the session if
+ // one exists. Otherwise we will check for a "remember me" cookie in this
+ // request, and if one exists, attempt to retrieve the user using that.
+ if (! is_null($id) && $this->user = $this->provider->retrieveById($id)) {
+ $this->fireAuthenticatedEvent($this->user);
+ }
+
+ // If the user is null, but we decrypt a "recaller" cookie we can attempt to
+ // pull the user data on that cookie which serves as a remember cookie on
+ // the application. Once we have a user we can return it to the caller.
+ if (is_null($this->user) && ! is_null($recaller = $this->recaller())) {
+ $this->user = $this->userFromRecaller($recaller);
+
+ if ($this->user) {
+ $this->updateSession($this->user->getAuthIdentifier());
+
+ $this->fireLoginEvent($this->user, true);
+ }
+ }
+
+ return $this->user;
+ }
+
+ /**
+ * Pull a user from the repository by its "remember me" cookie token.
+ *
+ * @param \Illuminate\Auth\Recaller $recaller
+ * @return mixed
+ */
+ protected function userFromRecaller($recaller)
+ {
+ if (! $recaller->valid() || $this->recallAttempted) {
+ return;
+ }
+
+ // If the user is null, but we decrypt a "recaller" cookie we can attempt to
+ // pull the user data on that cookie which serves as a remember cookie on
+ // the application. Once we have a user we can return it to the caller.
+ $this->recallAttempted = true;
+
+ $this->viaRemember = ! is_null($user = $this->provider->retrieveByToken(
+ $recaller->id(), $recaller->token()
+ ));
+
+ return $user;
+ }
+
+ /**
+ * Get the decrypted recaller cookie for the request.
+ *
+ * @return \Illuminate\Auth\Recaller|null
+ */
+ protected function recaller()
+ {
+ if (is_null($this->request)) {
+ return;
+ }
+
+ if ($recaller = $this->request->cookies->get($this->getRecallerName())) {
+ return new Recaller($recaller);
+ }
+ }
+
+ /**
+ * Get the ID for the currently authenticated user.
+ *
+ * @return int|string|null
+ */
+ public function id()
+ {
+ if ($this->loggedOut) {
+ return;
+ }
+
+ return $this->user()
+ ? $this->user()->getAuthIdentifier()
+ : $this->session->get($this->getName());
+ }
+
+ /**
+ * Log a user into the application without sessions or cookies.
+ *
+ * @param array $credentials
+ * @return bool
+ */
+ public function once(array $credentials = [])
+ {
+ $this->fireAttemptEvent($credentials);
+
+ if ($this->validate($credentials)) {
+ $this->setUser($this->lastAttempted);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Log the given user ID into the application without sessions or cookies.
+ *
+ * @param mixed $id
+ * @return \Illuminate\Contracts\Auth\Authenticatable|false
+ */
+ public function onceUsingId($id)
+ {
+ if (! is_null($user = $this->provider->retrieveById($id))) {
+ $this->setUser($user);
+
+ return $user;
+ }
+
+ return false;
+ }
+
+ /**
+ * Validate a user's credentials.
+ *
+ * @param array $credentials
+ * @return bool
+ */
+ public function validate(array $credentials = [])
+ {
+ $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
+
+ return $this->hasValidCredentials($user, $credentials);
+ }
+
+ /**
+ * Attempt to authenticate using HTTP Basic Auth.
+ *
+ * @param string $field
+ * @param array $extraConditions
+ * @return \Symfony\Component\HttpFoundation\Response|null
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
+ */
+ public function basic($field = 'email', $extraConditions = [])
+ {
+ if ($this->check()) {
+ return;
+ }
+
+ // If a username is set on the HTTP basic request, we will return out without
+ // interrupting the request lifecycle. Otherwise, we'll need to generate a
+ // request indicating that the given credentials were invalid for login.
+ if ($this->attemptBasic($this->getRequest(), $field, $extraConditions)) {
+ return;
+ }
+
+ return $this->failedBasicResponse();
+ }
+
+ /**
+ * Perform a stateless HTTP Basic login attempt.
+ *
+ * @param string $field
+ * @param array $extraConditions
+ * @return \Symfony\Component\HttpFoundation\Response|null
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
+ */
+ public function onceBasic($field = 'email', $extraConditions = [])
+ {
+ $credentials = $this->basicCredentials($this->getRequest(), $field);
+
+ if (! $this->once(array_merge($credentials, $extraConditions))) {
+ return $this->failedBasicResponse();
+ }
+ }
+
+ /**
+ * Attempt to authenticate using basic authentication.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * @param string $field
+ * @param array $extraConditions
+ * @return bool
+ */
+ protected function attemptBasic(Request $request, $field, $extraConditions = [])
+ {
+ if (! $request->getUser()) {
+ return false;
+ }
+
+ return $this->attempt(array_merge(
+ $this->basicCredentials($request, $field), $extraConditions
+ ));
+ }
+
+ /**
+ * Get the credential array for an HTTP Basic request.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * @param string $field
+ * @return array
+ */
+ protected function basicCredentials(Request $request, $field)
+ {
+ return [$field => $request->getUser(), 'password' => $request->getPassword()];
+ }
+
+ /**
+ * Get the response for basic authentication.
+ *
+ * @return void
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
+ */
+ protected function failedBasicResponse()
+ {
+ throw new UnauthorizedHttpException('Basic', 'Invalid credentials.');
+ }
+
+ /**
+ * Attempt to authenticate a user using the given credentials.
+ *
+ * @param array $credentials
+ * @param bool $remember
+ * @return bool
+ */
+ public function attempt(array $credentials = [], $remember = false)
+ {
+ $this->fireAttemptEvent($credentials, $remember);
+
+ $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
+
+ // If an implementation of UserInterface was returned, we'll ask the provider
+ // to validate the user against the given credentials, and if they are in
+ // fact valid we'll log the users into the application and return true.
+ if ($this->hasValidCredentials($user, $credentials)) {
+ $this->login($user, $remember);
+
+ return true;
+ }
+
+ // If the authentication attempt fails we will fire an event so that the user
+ // may be notified of any suspicious attempts to access their account from
+ // an unrecognized user. A developer may listen to this event as needed.
+ $this->fireFailedEvent($user, $credentials);
+
+ return false;
+ }
+
+ /**
+ * Attempt to authenticate a user with credentials and additional callbacks.
+ *
+ * @param array $credentials
+ * @param array|callable $callbacks
+ * @param bool $remember
+ * @return bool
+ */
+ public function attemptWhen(array $credentials = [], $callbacks = null, $remember = false)
+ {
+ $this->fireAttemptEvent($credentials, $remember);
+
+ $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);
+
+ // This method does the exact same thing as attempt, but also executes callbacks after
+ // the user is retrieved and validated. If one of the callbacks returns falsy we do
+ // not login the user. Instead, we will fail the specific authentication attempt.
+ if ($this->hasValidCredentials($user, $credentials) && $this->shouldLogin($callbacks, $user)) {
+ $this->login($user, $remember);
+
+ return true;
+ }
+
+ $this->fireFailedEvent($user, $credentials);
+
+ return false;
+ }
+
+ /**
+ * Determine if the user matches the credentials.
+ *
+ * @param mixed $user
+ * @param array $credentials
+ * @return bool
+ */
+ protected function hasValidCredentials($user, $credentials)
+ {
+ $validated = ! is_null($user) && $this->provider->validateCredentials($user, $credentials);
+
+ if ($validated) {
+ $this->fireValidatedEvent($user);
+ }
+
+ return $validated;
+ }
+
+ /**
+ * Determine if the user should login by executing the given callbacks.
+ *
+ * @param array|callable|null $callbacks
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @return bool
+ */
+ protected function shouldLogin($callbacks, AuthenticatableContract $user)
+ {
+ foreach (Arr::wrap($callbacks) as $callback) {
+ if (! $callback($user, $this)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Log the given user ID into the application.
+ *
+ * @param mixed $id
+ * @param bool $remember
+ * @return \Illuminate\Contracts\Auth\Authenticatable|false
+ */
+ public function loginUsingId($id, $remember = false)
+ {
+ if (! is_null($user = $this->provider->retrieveById($id))) {
+ $this->login($user, $remember);
+
+ return $user;
+ }
+
+ return false;
+ }
+
+ /**
+ * Log a user into the application.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param bool $remember
+ * @return void
+ */
+ public function login(AuthenticatableContract $user, $remember = false)
+ {
+ $this->updateSession($user->getAuthIdentifier());
+
+ // If the user should be permanently "remembered" by the application we will
+ // queue a permanent cookie that contains the encrypted copy of the user
+ // identifier. We will then decrypt this later to retrieve the users.
+ if ($remember) {
+ $this->ensureRememberTokenIsSet($user);
+
+ $this->queueRecallerCookie($user);
+ }
+
+ // If we have an event dispatcher instance set we will fire an event so that
+ // any listeners will hook into the authentication events and run actions
+ // based on the login and logout events fired from the guard instances.
+ $this->fireLoginEvent($user, $remember);
+
+ $this->setUser($user);
+ }
+
+ /**
+ * Update the session with the given ID.
+ *
+ * @param string $id
+ * @return void
+ */
+ protected function updateSession($id)
+ {
+ $this->session->put($this->getName(), $id);
+
+ $this->session->migrate(true);
+ }
+
+ /**
+ * Create a new "remember me" token for the user if one doesn't already exist.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @return void
+ */
+ protected function ensureRememberTokenIsSet(AuthenticatableContract $user)
+ {
+ if (empty($user->getRememberToken())) {
+ $this->cycleRememberToken($user);
+ }
+ }
+
+ /**
+ * Queue the recaller cookie into the cookie jar.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @return void
+ */
+ protected function queueRecallerCookie(AuthenticatableContract $user)
+ {
+ $this->getCookieJar()->queue($this->createRecaller(
+ $user->getAuthIdentifier().'|'.$user->getRememberToken().'|'.$user->getAuthPassword()
+ ));
+ }
+
+ /**
+ * Create a "remember me" cookie for a given ID.
+ *
+ * @param string $value
+ * @return \Symfony\Component\HttpFoundation\Cookie
+ */
+ protected function createRecaller($value)
+ {
+ return $this->getCookieJar()->make($this->getRecallerName(), $value, $this->getRememberDuration());
+ }
+
+ /**
+ * Log the user out of the application.
+ *
+ * @return void
+ */
+ public function logout()
+ {
+ $user = $this->user();
+
+ $this->clearUserDataFromStorage();
+
+ if (! is_null($this->user) && ! empty($user->getRememberToken())) {
+ $this->cycleRememberToken($user);
+ }
+
+ // If we have an event dispatcher instance, we can fire off the logout event
+ // so any further processing can be done. This allows the developer to be
+ // listening for anytime a user signs out of this application manually.
+ if (isset($this->events)) {
+ $this->events->dispatch(new Logout($this->name, $user));
+ }
+
+ // Once we have fired the logout event we will clear the users out of memory
+ // so they are no longer available as the user is no longer considered as
+ // being signed into this application and should not be available here.
+ $this->user = null;
+
+ $this->loggedOut = true;
+ }
+
+ /**
+ * Log the user out of the application on their current device only.
+ *
+ * This method does not cycle the "remember" token.
+ *
+ * @return void
+ */
+ public function logoutCurrentDevice()
+ {
+ $user = $this->user();
+
+ $this->clearUserDataFromStorage();
+
+ // If we have an event dispatcher instance, we can fire off the logout event
+ // so any further processing can be done. This allows the developer to be
+ // listening for anytime a user signs out of this application manually.
+ if (isset($this->events)) {
+ $this->events->dispatch(new CurrentDeviceLogout($this->name, $user));
+ }
+
+ // Once we have fired the logout event we will clear the users out of memory
+ // so they are no longer available as the user is no longer considered as
+ // being signed into this application and should not be available here.
+ $this->user = null;
+
+ $this->loggedOut = true;
+ }
+
+ /**
+ * Remove the user data from the session and cookies.
+ *
+ * @return void
+ */
+ protected function clearUserDataFromStorage()
+ {
+ $this->session->remove($this->getName());
+
+ if (! is_null($this->recaller())) {
+ $this->getCookieJar()->queue($this->getCookieJar()
+ ->forget($this->getRecallerName()));
+ }
+ }
+
+ /**
+ * Refresh the "remember me" token for the user.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @return void
+ */
+ protected function cycleRememberToken(AuthenticatableContract $user)
+ {
+ $user->setRememberToken($token = Str::random(60));
+
+ $this->provider->updateRememberToken($user, $token);
+ }
+
+ /**
+ * Invalidate other sessions for the current user.
+ *
+ * The application must be using the AuthenticateSession middleware.
+ *
+ * @param string $password
+ * @param string $attribute
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ *
+ * @throws \Illuminate\Auth\AuthenticationException
+ */
+ public function logoutOtherDevices($password, $attribute = 'password')
+ {
+ if (! $this->user()) {
+ return;
+ }
+
+ $result = $this->rehashUserPassword($password, $attribute);
+
+ if ($this->recaller() ||
+ $this->getCookieJar()->hasQueued($this->getRecallerName())) {
+ $this->queueRecallerCookie($this->user());
+ }
+
+ $this->fireOtherDeviceLogoutEvent($this->user());
+
+ return $result;
+ }
+
+ /**
+ * Rehash the current user's password.
+ *
+ * @param string $password
+ * @param string $attribute
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function rehashUserPassword($password, $attribute)
+ {
+ if (! Hash::check($password, $this->user()->{$attribute})) {
+ throw new InvalidArgumentException('The given password does not match the current password.');
+ }
+
+ return tap($this->user()->forceFill([
+ $attribute => Hash::make($password),
+ ]))->save();
+ }
+
+ /**
+ * Register an authentication attempt event listener.
+ *
+ * @param mixed $callback
+ * @return void
+ */
+ public function attempting($callback)
+ {
+ $this->events?->listen(Events\Attempting::class, $callback);
+ }
+
+ /**
+ * Fire the attempt event with the arguments.
+ *
+ * @param array $credentials
+ * @param bool $remember
+ * @return void
+ */
+ protected function fireAttemptEvent(array $credentials, $remember = false)
+ {
+ $this->events?->dispatch(new Attempting($this->name, $credentials, $remember));
+ }
+
+ /**
+ * Fires the validated event if the dispatcher is set.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @return void
+ */
+ protected function fireValidatedEvent($user)
+ {
+ $this->events?->dispatch(new Validated($this->name, $user));
+ }
+
+ /**
+ * Fire the login event if the dispatcher is set.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @param bool $remember
+ * @return void
+ */
+ protected function fireLoginEvent($user, $remember = false)
+ {
+ $this->events?->dispatch(new Login($this->name, $user, $remember));
+ }
+
+ /**
+ * Fire the authenticated event if the dispatcher is set.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @return void
+ */
+ protected function fireAuthenticatedEvent($user)
+ {
+ $this->events?->dispatch(new Authenticated($this->name, $user));
+ }
+
+ /**
+ * Fire the other device logout event if the dispatcher is set.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @return void
+ */
+ protected function fireOtherDeviceLogoutEvent($user)
+ {
+ $this->events?->dispatch(new OtherDeviceLogout($this->name, $user));
+ }
+
+ /**
+ * Fire the failed authentication attempt event with the given arguments.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable|null $user
+ * @param array $credentials
+ * @return void
+ */
+ protected function fireFailedEvent($user, array $credentials)
+ {
+ $this->events?->dispatch(new Failed($this->name, $user, $credentials));
+ }
+
+ /**
+ * Get the last user we attempted to authenticate.
+ *
+ * @return \Illuminate\Contracts\Auth\Authenticatable
+ */
+ public function getLastAttempted()
+ {
+ return $this->lastAttempted;
+ }
+
+ /**
+ * Get a unique identifier for the auth session value.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return 'login_'.$this->name.'_'.sha1(static::class);
+ }
+
+ /**
+ * Get the name of the cookie used to store the "recaller".
+ *
+ * @return string
+ */
+ public function getRecallerName()
+ {
+ return 'remember_'.$this->name.'_'.sha1(static::class);
+ }
+
+ /**
+ * Determine if the user was authenticated via "remember me" cookie.
+ *
+ * @return bool
+ */
+ public function viaRemember()
+ {
+ return $this->viaRemember;
+ }
+
+ /**
+ * Get the number of minutes the remember me cookie should be valid for.
+ *
+ * @return int
+ */
+ protected function getRememberDuration()
+ {
+ return $this->rememberDuration;
+ }
+
+ /**
+ * Set the number of minutes the remember me cookie should be valid for.
+ *
+ * @param int $minutes
+ * @return $this
+ */
+ public function setRememberDuration($minutes)
+ {
+ $this->rememberDuration = $minutes;
+
+ return $this;
+ }
+
+ /**
+ * Get the cookie creator instance used by the guard.
+ *
+ * @return \Illuminate\Contracts\Cookie\QueueingFactory
+ *
+ * @throws \RuntimeException
+ */
+ public function getCookieJar()
+ {
+ if (! isset($this->cookie)) {
+ throw new RuntimeException('Cookie jar has not been set.');
+ }
+
+ return $this->cookie;
+ }
+
+ /**
+ * Set the cookie creator instance used by the guard.
+ *
+ * @param \Illuminate\Contracts\Cookie\QueueingFactory $cookie
+ * @return void
+ */
+ public function setCookieJar(CookieJar $cookie)
+ {
+ $this->cookie = $cookie;
+ }
+
+ /**
+ * Get the event dispatcher instance.
+ *
+ * @return \Illuminate\Contracts\Events\Dispatcher
+ */
+ public function getDispatcher()
+ {
+ return $this->events;
+ }
+
+ /**
+ * Set the event dispatcher instance.
+ *
+ * @param \Illuminate\Contracts\Events\Dispatcher $events
+ * @return void
+ */
+ public function setDispatcher(Dispatcher $events)
+ {
+ $this->events = $events;
+ }
+
+ /**
+ * Get the session store used by the guard.
+ *
+ * @return \Illuminate\Contracts\Session\Session
+ */
+ public function getSession()
+ {
+ return $this->session;
+ }
+
+ /**
+ * Return the currently cached user.
+ *
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function getUser()
+ {
+ return $this->user;
+ }
+
+ /**
+ * Set the current user.
+ *
+ * @param \Illuminate\Contracts\Auth\Authenticatable $user
+ * @return $this
+ */
+ public function setUser(AuthenticatableContract $user)
+ {
+ $this->user = $user;
+
+ $this->loggedOut = false;
+
+ $this->fireAuthenticatedEvent($user);
+
+ return $this;
+ }
+
+ /**
+ * Get the current request instance.
+ *
+ * @return \Symfony\Component\HttpFoundation\Request
+ */
+ public function getRequest()
+ {
+ return $this->request ?: Request::createFromGlobals();
+ }
+
+ /**
+ * Set the current request instance.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ * @return $this
+ */
+ public function setRequest(Request $request)
+ {
+ $this->request = $request;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/TokenGuard.php b/vendor/laravel/framework/src/Illuminate/Auth/TokenGuard.php
new file mode 100644
index 0000000..b1aa7a7
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/TokenGuard.php
@@ -0,0 +1,149 @@
+hash = $hash;
+ $this->request = $request;
+ $this->provider = $provider;
+ $this->inputKey = $inputKey;
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * Get the currently authenticated user.
+ *
+ * @return \Illuminate\Contracts\Auth\Authenticatable|null
+ */
+ public function user()
+ {
+ // If we've already retrieved the user for the current request we can just
+ // return it back immediately. We do not want to fetch the user data on
+ // every call to this method because that would be tremendously slow.
+ if (! is_null($this->user)) {
+ return $this->user;
+ }
+
+ $user = null;
+
+ $token = $this->getTokenForRequest();
+
+ if (! empty($token)) {
+ $user = $this->provider->retrieveByCredentials([
+ $this->storageKey => $this->hash ? hash('sha256', $token) : $token,
+ ]);
+ }
+
+ return $this->user = $user;
+ }
+
+ /**
+ * Get the token for the current request.
+ *
+ * @return string
+ */
+ public function getTokenForRequest()
+ {
+ $token = $this->request->query($this->inputKey);
+
+ if (empty($token)) {
+ $token = $this->request->input($this->inputKey);
+ }
+
+ if (empty($token)) {
+ $token = $this->request->bearerToken();
+ }
+
+ if (empty($token)) {
+ $token = $this->request->getPassword();
+ }
+
+ return $token;
+ }
+
+ /**
+ * Validate a user's credentials.
+ *
+ * @param array $credentials
+ * @return bool
+ */
+ public function validate(array $credentials = [])
+ {
+ if (empty($credentials[$this->inputKey])) {
+ return false;
+ }
+
+ $credentials = [$this->storageKey => $credentials[$this->inputKey]];
+
+ if ($this->provider->retrieveByCredentials($credentials)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Set the current request instance.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return $this
+ */
+ public function setRequest(Request $request)
+ {
+ $this->request = $request;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Auth/composer.json b/vendor/laravel/framework/src/Illuminate/Auth/composer.json
new file mode 100644
index 0000000..ae928a3
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Auth/composer.json
@@ -0,0 +1,44 @@
+{
+ "name": "illuminate/auth",
+ "description": "The Illuminate Auth package.",
+ "license": "MIT",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^8.0.2",
+ "illuminate/collections": "^9.0",
+ "illuminate/contracts": "^9.0",
+ "illuminate/http": "^9.0",
+ "illuminate/macroable": "^9.0",
+ "illuminate/queue": "^9.0",
+ "illuminate/support": "^9.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Auth\\": ""
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "suggest": {
+ "illuminate/console": "Required to use the auth:clear-resets command (^9.0).",
+ "illuminate/queue": "Required to fire login / logout events (^9.0).",
+ "illuminate/session": "Required to use the session based guard (^9.0)."
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "minimum-stability": "dev"
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastController.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastController.php
new file mode 100644
index 0000000..69274b7
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastController.php
@@ -0,0 +1,44 @@
+hasSession()) {
+ $request->session()->reflash();
+ }
+
+ return Broadcast::auth($request);
+ }
+
+ /**
+ * Authenticate the current user.
+ *
+ * See: https://pusher.com/docs/channels/server_api/authenticating-users/#user-authentication.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\Response
+ */
+ public function authenticateUser(Request $request)
+ {
+ if ($request->hasSession()) {
+ $request->session()->reflash();
+ }
+
+ return Broadcast::resolveAuthenticatedUser($request)
+ ?? throw new AccessDeniedHttpException;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastEvent.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastEvent.php
new file mode 100644
index 0000000..fef5423
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastEvent.php
@@ -0,0 +1,148 @@
+event = $event;
+ $this->tries = property_exists($event, 'tries') ? $event->tries : null;
+ $this->timeout = property_exists($event, 'timeout') ? $event->timeout : null;
+ $this->backoff = property_exists($event, 'backoff') ? $event->backoff : null;
+ $this->afterCommit = property_exists($event, 'afterCommit') ? $event->afterCommit : null;
+ }
+
+ /**
+ * Handle the queued job.
+ *
+ * @param \Illuminate\Contracts\Broadcasting\Factory $manager
+ * @return void
+ */
+ public function handle(BroadcastingFactory $manager)
+ {
+ $name = method_exists($this->event, 'broadcastAs')
+ ? $this->event->broadcastAs() : get_class($this->event);
+
+ $channels = Arr::wrap($this->event->broadcastOn());
+
+ if (empty($channels)) {
+ return;
+ }
+
+ $connections = method_exists($this->event, 'broadcastConnections')
+ ? $this->event->broadcastConnections()
+ : [null];
+
+ $payload = $this->getPayloadFromEvent($this->event);
+
+ foreach ($connections as $connection) {
+ $manager->connection($connection)->broadcast(
+ $channels, $name, $payload
+ );
+ }
+ }
+
+ /**
+ * Get the payload for the given event.
+ *
+ * @param mixed $event
+ * @return array
+ */
+ protected function getPayloadFromEvent($event)
+ {
+ if (method_exists($event, 'broadcastWith') &&
+ ! is_null($payload = $event->broadcastWith())) {
+ return array_merge($payload, ['socket' => data_get($event, 'socket')]);
+ }
+
+ $payload = [];
+
+ foreach ((new ReflectionClass($event))->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
+ $payload[$property->getName()] = $this->formatProperty($property->getValue($event));
+ }
+
+ unset($payload['broadcastQueue']);
+
+ return $payload;
+ }
+
+ /**
+ * Format the given value for a property.
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function formatProperty($value)
+ {
+ if ($value instanceof Arrayable) {
+ return $value->toArray();
+ }
+
+ return $value;
+ }
+
+ /**
+ * Get the display name for the queued job.
+ *
+ * @return string
+ */
+ public function displayName()
+ {
+ return get_class($this->event);
+ }
+
+ /**
+ * Prepare the instance for cloning.
+ *
+ * @return void
+ */
+ public function __clone()
+ {
+ $this->event = clone $this->event;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastException.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastException.php
new file mode 100644
index 0000000..8fd55f7
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastException.php
@@ -0,0 +1,10 @@
+app = $app;
+ }
+
+ /**
+ * Register the routes for handling broadcast channel authentication and sockets.
+ *
+ * @param array|null $attributes
+ * @return void
+ */
+ public function routes(array $attributes = null)
+ {
+ if ($this->app instanceof CachesRoutes && $this->app->routesAreCached()) {
+ return;
+ }
+
+ $attributes = $attributes ?: ['middleware' => ['web']];
+
+ $this->app['router']->group($attributes, function ($router) {
+ $router->match(
+ ['get', 'post'], '/broadcasting/auth',
+ '\\'.BroadcastController::class.'@authenticate'
+ )->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class]);
+ });
+ }
+
+ /**
+ * Register the routes for handling broadcast user authentication.
+ *
+ * @param array|null $attributes
+ * @return void
+ */
+ public function userRoutes(array $attributes = null)
+ {
+ if ($this->app instanceof CachesRoutes && $this->app->routesAreCached()) {
+ return;
+ }
+
+ $attributes = $attributes ?: ['middleware' => ['web']];
+
+ $this->app['router']->group($attributes, function ($router) {
+ $router->match(
+ ['get', 'post'], '/broadcasting/user-auth',
+ '\\'.BroadcastController::class.'@authenticateUser'
+ )->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class]);
+ });
+ }
+
+ /**
+ * Register the routes for handling broadcast authentication and sockets.
+ *
+ * Alias of "routes" method.
+ *
+ * @param array|null $attributes
+ * @return void
+ */
+ public function channelRoutes(array $attributes = null)
+ {
+ return $this->routes($attributes);
+ }
+
+ /**
+ * Get the socket ID for the given request.
+ *
+ * @param \Illuminate\Http\Request|null $request
+ * @return string|null
+ */
+ public function socket($request = null)
+ {
+ if (! $request && ! $this->app->bound('request')) {
+ return;
+ }
+
+ $request = $request ?: $this->app['request'];
+
+ return $request->header('X-Socket-ID');
+ }
+
+ /**
+ * Begin broadcasting an event.
+ *
+ * @param mixed|null $event
+ * @return \Illuminate\Broadcasting\PendingBroadcast
+ */
+ public function event($event = null)
+ {
+ return new PendingBroadcast($this->app->make('events'), $event);
+ }
+
+ /**
+ * Queue the given event for broadcast.
+ *
+ * @param mixed $event
+ * @return void
+ */
+ public function queue($event)
+ {
+ if ($event instanceof ShouldBroadcastNow ||
+ (is_object($event) &&
+ method_exists($event, 'shouldBroadcastNow') &&
+ $event->shouldBroadcastNow())) {
+ return $this->app->make(BusDispatcherContract::class)->dispatchNow(new BroadcastEvent(clone $event));
+ }
+
+ $queue = null;
+
+ if (method_exists($event, 'broadcastQueue')) {
+ $queue = $event->broadcastQueue();
+ } elseif (isset($event->broadcastQueue)) {
+ $queue = $event->broadcastQueue;
+ } elseif (isset($event->queue)) {
+ $queue = $event->queue;
+ }
+
+ $this->app->make('queue')->connection($event->connection ?? null)->pushOn(
+ $queue, new BroadcastEvent(clone $event)
+ );
+ }
+
+ /**
+ * Get a driver instance.
+ *
+ * @param string|null $driver
+ * @return mixed
+ */
+ public function connection($driver = null)
+ {
+ return $this->driver($driver);
+ }
+
+ /**
+ * Get a driver instance.
+ *
+ * @param string|null $name
+ * @return mixed
+ */
+ public function driver($name = null)
+ {
+ $name = $name ?: $this->getDefaultDriver();
+
+ return $this->drivers[$name] = $this->get($name);
+ }
+
+ /**
+ * Attempt to get the connection from the local cache.
+ *
+ * @param string $name
+ * @return \Illuminate\Contracts\Broadcasting\Broadcaster
+ */
+ protected function get($name)
+ {
+ return $this->drivers[$name] ?? $this->resolve($name);
+ }
+
+ /**
+ * Resolve the given broadcaster.
+ *
+ * @param string $name
+ * @return \Illuminate\Contracts\Broadcasting\Broadcaster
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function resolve($name)
+ {
+ $config = $this->getConfig($name);
+
+ if (isset($this->customCreators[$config['driver']])) {
+ return $this->callCustomCreator($config);
+ }
+
+ $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
+
+ if (! method_exists($this, $driverMethod)) {
+ throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
+ }
+
+ return $this->{$driverMethod}($config);
+ }
+
+ /**
+ * Call a custom driver creator.
+ *
+ * @param array $config
+ * @return mixed
+ */
+ protected function callCustomCreator(array $config)
+ {
+ return $this->customCreators[$config['driver']]($this->app, $config);
+ }
+
+ /**
+ * Create an instance of the driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Contracts\Broadcasting\Broadcaster
+ */
+ protected function createPusherDriver(array $config)
+ {
+ return new PusherBroadcaster($this->pusher($config));
+ }
+
+ /**
+ * Get a Pusher instance for the given configuration.
+ *
+ * @param array $config
+ * @return \Pusher\Pusher
+ */
+ public function pusher(array $config)
+ {
+ $pusher = new Pusher(
+ $config['key'],
+ $config['secret'],
+ $config['app_id'],
+ $config['options'] ?? [],
+ isset($config['client_options']) && ! empty($config['client_options'])
+ ? new GuzzleClient($config['client_options'])
+ : null,
+ );
+
+ if ($config['log'] ?? false) {
+ $pusher->setLogger($this->app->make(LoggerInterface::class));
+ }
+
+ return $pusher;
+ }
+
+ /**
+ * Create an instance of the driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Contracts\Broadcasting\Broadcaster
+ */
+ protected function createAblyDriver(array $config)
+ {
+ return new AblyBroadcaster($this->ably($config));
+ }
+
+ /**
+ * Get an Ably instance for the given configuration.
+ *
+ * @param array $config
+ * @return \Ably\AblyRest
+ */
+ public function ably(array $config)
+ {
+ return new AblyRest($config);
+ }
+
+ /**
+ * Create an instance of the driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Contracts\Broadcasting\Broadcaster
+ */
+ protected function createRedisDriver(array $config)
+ {
+ return new RedisBroadcaster(
+ $this->app->make('redis'), $config['connection'] ?? null,
+ $this->app['config']->get('database.redis.options.prefix', '')
+ );
+ }
+
+ /**
+ * Create an instance of the driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Contracts\Broadcasting\Broadcaster
+ */
+ protected function createLogDriver(array $config)
+ {
+ return new LogBroadcaster(
+ $this->app->make(LoggerInterface::class)
+ );
+ }
+
+ /**
+ * Create an instance of the driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Contracts\Broadcasting\Broadcaster
+ */
+ protected function createNullDriver(array $config)
+ {
+ return new NullBroadcaster;
+ }
+
+ /**
+ * Get the connection configuration.
+ *
+ * @param string $name
+ * @return array
+ */
+ protected function getConfig($name)
+ {
+ if (! is_null($name) && $name !== 'null') {
+ return $this->app['config']["broadcasting.connections.{$name}"];
+ }
+
+ return ['driver' => 'null'];
+ }
+
+ /**
+ * Get the default driver name.
+ *
+ * @return string
+ */
+ public function getDefaultDriver()
+ {
+ return $this->app['config']['broadcasting.default'];
+ }
+
+ /**
+ * Set the default driver name.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function setDefaultDriver($name)
+ {
+ $this->app['config']['broadcasting.default'] = $name;
+ }
+
+ /**
+ * Disconnect the given disk and remove from local cache.
+ *
+ * @param string|null $name
+ * @return void
+ */
+ public function purge($name = null)
+ {
+ $name ??= $this->getDefaultDriver();
+
+ unset($this->drivers[$name]);
+ }
+
+ /**
+ * Register a custom driver creator Closure.
+ *
+ * @param string $driver
+ * @param \Closure $callback
+ * @return $this
+ */
+ public function extend($driver, Closure $callback)
+ {
+ $this->customCreators[$driver] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Get the application instance used by the manager.
+ *
+ * @return \Illuminate\Contracts\Foundation\Application
+ */
+ public function getApplication()
+ {
+ return $this->app;
+ }
+
+ /**
+ * Set the application instance used by the manager.
+ *
+ * @param \Illuminate\Contracts\Foundation\Application $app
+ * @return $this
+ */
+ public function setApplication($app)
+ {
+ $this->app = $app;
+
+ return $this;
+ }
+
+ /**
+ * Forget all of the resolved driver instances.
+ *
+ * @return $this
+ */
+ public function forgetDrivers()
+ {
+ $this->drivers = [];
+
+ return $this;
+ }
+
+ /**
+ * Dynamically call the default driver instance.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->driver()->$method(...$parameters);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastServiceProvider.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastServiceProvider.php
new file mode 100644
index 0000000..e6897e2
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastServiceProvider.php
@@ -0,0 +1,45 @@
+app->singleton(BroadcastManager::class, function ($app) {
+ return new BroadcastManager($app);
+ });
+
+ $this->app->singleton(BroadcasterContract::class, function ($app) {
+ return $app->make(BroadcastManager::class)->connection();
+ });
+
+ $this->app->alias(
+ BroadcastManager::class, BroadcastingFactory::class
+ );
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides()
+ {
+ return [
+ BroadcastManager::class,
+ BroadcastingFactory::class,
+ BroadcasterContract::class,
+ ];
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/AblyBroadcaster.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/AblyBroadcaster.php
new file mode 100644
index 0000000..3a6db78
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/AblyBroadcaster.php
@@ -0,0 +1,235 @@
+ably = $ably;
+ }
+
+ /**
+ * Authenticate the incoming request for a given channel.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return mixed
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+ */
+ public function auth($request)
+ {
+ $channelName = $this->normalizeChannelName($request->channel_name);
+
+ if (empty($request->channel_name) ||
+ ($this->isGuardedChannel($request->channel_name) &&
+ ! $this->retrieveUser($request, $channelName))) {
+ throw new AccessDeniedHttpException;
+ }
+
+ return parent::verifyUserCanAccessChannel(
+ $request, $channelName
+ );
+ }
+
+ /**
+ * Return the valid authentication response.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param mixed $result
+ * @return mixed
+ */
+ public function validAuthenticationResponse($request, $result)
+ {
+ if (str_starts_with($request->channel_name, 'private')) {
+ $signature = $this->generateAblySignature(
+ $request->channel_name, $request->socket_id
+ );
+
+ return ['auth' => $this->getPublicToken().':'.$signature];
+ }
+
+ $channelName = $this->normalizeChannelName($request->channel_name);
+
+ $user = $this->retrieveUser($request, $channelName);
+
+ $broadcastIdentifier = method_exists($user, 'getAuthIdentifierForBroadcasting')
+ ? $user->getAuthIdentifierForBroadcasting()
+ : $user->getAuthIdentifier();
+
+ $signature = $this->generateAblySignature(
+ $request->channel_name,
+ $request->socket_id,
+ $userData = array_filter([
+ 'user_id' => (string) $broadcastIdentifier,
+ 'user_info' => $result,
+ ])
+ );
+
+ return [
+ 'auth' => $this->getPublicToken().':'.$signature,
+ 'channel_data' => json_encode($userData),
+ ];
+ }
+
+ /**
+ * Generate the signature needed for Ably authentication headers.
+ *
+ * @param string $channelName
+ * @param string $socketId
+ * @param array|null $userData
+ * @return string
+ */
+ public function generateAblySignature($channelName, $socketId, $userData = null)
+ {
+ return hash_hmac(
+ 'sha256',
+ sprintf('%s:%s%s', $socketId, $channelName, $userData ? ':'.json_encode($userData) : ''),
+ $this->getPrivateToken(),
+ );
+ }
+
+ /**
+ * Broadcast the given event.
+ *
+ * @param array $channels
+ * @param string $event
+ * @param array $payload
+ * @return void
+ *
+ * @throws \Illuminate\Broadcasting\BroadcastException
+ */
+ public function broadcast(array $channels, $event, array $payload = [])
+ {
+ try {
+ foreach ($this->formatChannels($channels) as $channel) {
+ $this->ably->channels->get($channel)->publish(
+ $this->buildAblyMessage($event, $payload)
+ );
+ }
+ } catch (AblyException $e) {
+ throw new BroadcastException(
+ sprintf('Ably error: %s', $e->getMessage())
+ );
+ }
+ }
+
+ /**
+ * Build an Ably message object for broadcasting.
+ *
+ * @param string $event
+ * @param array $payload
+ * @return \Ably\Models\Message
+ */
+ protected function buildAblyMessage($event, array $payload = [])
+ {
+ return tap(new AblyMessage, function ($message) use ($event, $payload) {
+ $message->name = $event;
+ $message->data = $payload;
+ $message->connectionKey = data_get($payload, 'socket');
+ });
+ }
+
+ /**
+ * Return true if the channel is protected by authentication.
+ *
+ * @param string $channel
+ * @return bool
+ */
+ public function isGuardedChannel($channel)
+ {
+ return Str::startsWith($channel, ['private-', 'presence-']);
+ }
+
+ /**
+ * Remove prefix from channel name.
+ *
+ * @param string $channel
+ * @return string
+ */
+ public function normalizeChannelName($channel)
+ {
+ if ($this->isGuardedChannel($channel)) {
+ return str_starts_with($channel, 'private-')
+ ? Str::replaceFirst('private-', '', $channel)
+ : Str::replaceFirst('presence-', '', $channel);
+ }
+
+ return $channel;
+ }
+
+ /**
+ * Format the channel array into an array of strings.
+ *
+ * @param array $channels
+ * @return array
+ */
+ protected function formatChannels(array $channels)
+ {
+ return array_map(function ($channel) {
+ $channel = (string) $channel;
+
+ if (Str::startsWith($channel, ['private-', 'presence-'])) {
+ return str_starts_with($channel, 'private-')
+ ? Str::replaceFirst('private-', 'private:', $channel)
+ : Str::replaceFirst('presence-', 'presence:', $channel);
+ }
+
+ return 'public:'.$channel;
+ }, $channels);
+ }
+
+ /**
+ * Get the public token value from the Ably key.
+ *
+ * @return mixed
+ */
+ protected function getPublicToken()
+ {
+ return Str::before($this->ably->options->key, ':');
+ }
+
+ /**
+ * Get the private token value from the Ably key.
+ *
+ * @return mixed
+ */
+ protected function getPrivateToken()
+ {
+ return Str::after($this->ably->options->key, ':');
+ }
+
+ /**
+ * Get the underlying Ably SDK instance.
+ *
+ * @return \Ably\AblyRest
+ */
+ public function getAbly()
+ {
+ return $this->ably;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php
new file mode 100644
index 0000000..3cd1cd9
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/Broadcaster.php
@@ -0,0 +1,373 @@
+authenticatedUserCallback) {
+ return $this->authenticatedUserCallback->__invoke($request);
+ }
+ }
+
+ /**
+ * Register the user retrieval callback used to authenticate connections.
+ *
+ * See: https://pusher.com/docs/channels/library_auth_reference/auth-signatures/#user-authentication.
+ *
+ * @param \Closure $callback
+ * @return void
+ */
+ public function resolveAuthenticatedUserUsing(Closure $callback)
+ {
+ $this->authenticatedUserCallback = $callback;
+ }
+
+ /**
+ * Register a channel authenticator.
+ *
+ * @param \Illuminate\Contracts\Broadcasting\HasBroadcastChannel|string $channel
+ * @param callable|string $callback
+ * @param array $options
+ * @return $this
+ */
+ public function channel($channel, $callback, $options = [])
+ {
+ if ($channel instanceof HasBroadcastChannel) {
+ $channel = $channel->broadcastChannelRoute();
+ } elseif (is_string($channel) && class_exists($channel) && is_a($channel, HasBroadcastChannel::class, true)) {
+ $channel = (new $channel)->broadcastChannelRoute();
+ }
+
+ $this->channels[$channel] = $callback;
+
+ $this->channelOptions[$channel] = $options;
+
+ return $this;
+ }
+
+ /**
+ * Authenticate the incoming request for a given channel.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param string $channel
+ * @return mixed
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+ */
+ protected function verifyUserCanAccessChannel($request, $channel)
+ {
+ foreach ($this->channels as $pattern => $callback) {
+ if (! $this->channelNameMatchesPattern($channel, $pattern)) {
+ continue;
+ }
+
+ $parameters = $this->extractAuthParameters($pattern, $channel, $callback);
+
+ $handler = $this->normalizeChannelHandlerToCallable($callback);
+
+ if ($result = $handler($this->retrieveUser($request, $channel), ...$parameters)) {
+ return $this->validAuthenticationResponse($request, $result);
+ }
+ }
+
+ throw new AccessDeniedHttpException;
+ }
+
+ /**
+ * Extract the parameters from the given pattern and channel.
+ *
+ * @param string $pattern
+ * @param string $channel
+ * @param callable|string $callback
+ * @return array
+ */
+ protected function extractAuthParameters($pattern, $channel, $callback)
+ {
+ $callbackParameters = $this->extractParameters($callback);
+
+ return collect($this->extractChannelKeys($pattern, $channel))->reject(function ($value, $key) {
+ return is_numeric($key);
+ })->map(function ($value, $key) use ($callbackParameters) {
+ return $this->resolveBinding($key, $value, $callbackParameters);
+ })->values()->all();
+ }
+
+ /**
+ * Extracts the parameters out of what the user passed to handle the channel authentication.
+ *
+ * @param callable|string $callback
+ * @return \ReflectionParameter[]
+ *
+ * @throws \Exception
+ */
+ protected function extractParameters($callback)
+ {
+ if (is_callable($callback)) {
+ return (new ReflectionFunction($callback))->getParameters();
+ } elseif (is_string($callback)) {
+ return $this->extractParametersFromClass($callback);
+ }
+
+ throw new Exception('Given channel handler is an unknown type.');
+ }
+
+ /**
+ * Extracts the parameters out of a class channel's "join" method.
+ *
+ * @param string $callback
+ * @return \ReflectionParameter[]
+ *
+ * @throws \Exception
+ */
+ protected function extractParametersFromClass($callback)
+ {
+ $reflection = new ReflectionClass($callback);
+
+ if (! $reflection->hasMethod('join')) {
+ throw new Exception('Class based channel must define a "join" method.');
+ }
+
+ return $reflection->getMethod('join')->getParameters();
+ }
+
+ /**
+ * Extract the channel keys from the incoming channel name.
+ *
+ * @param string $pattern
+ * @param string $channel
+ * @return array
+ */
+ protected function extractChannelKeys($pattern, $channel)
+ {
+ preg_match('/^'.preg_replace('/\{(.*?)\}/', '(?<$1>[^\.]+)', $pattern).'/', $channel, $keys);
+
+ return $keys;
+ }
+
+ /**
+ * Resolve the given parameter binding.
+ *
+ * @param string $key
+ * @param string $value
+ * @param array $callbackParameters
+ * @return mixed
+ */
+ protected function resolveBinding($key, $value, $callbackParameters)
+ {
+ $newValue = $this->resolveExplicitBindingIfPossible($key, $value);
+
+ return $newValue === $value ? $this->resolveImplicitBindingIfPossible(
+ $key, $value, $callbackParameters
+ ) : $newValue;
+ }
+
+ /**
+ * Resolve an explicit parameter binding if applicable.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function resolveExplicitBindingIfPossible($key, $value)
+ {
+ $binder = $this->binder();
+
+ if ($binder && $binder->getBindingCallback($key)) {
+ return call_user_func($binder->getBindingCallback($key), $value);
+ }
+
+ return $value;
+ }
+
+ /**
+ * Resolve an implicit parameter binding if applicable.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param array $callbackParameters
+ * @return mixed
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+ */
+ protected function resolveImplicitBindingIfPossible($key, $value, $callbackParameters)
+ {
+ foreach ($callbackParameters as $parameter) {
+ if (! $this->isImplicitlyBindable($key, $parameter)) {
+ continue;
+ }
+
+ $className = Reflector::getParameterClassName($parameter);
+
+ if (is_null($model = (new $className)->resolveRouteBinding($value))) {
+ throw new AccessDeniedHttpException;
+ }
+
+ return $model;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Determine if a given key and parameter is implicitly bindable.
+ *
+ * @param string $key
+ * @param \ReflectionParameter $parameter
+ * @return bool
+ */
+ protected function isImplicitlyBindable($key, $parameter)
+ {
+ return $parameter->getName() === $key &&
+ Reflector::isParameterSubclassOf($parameter, UrlRoutable::class);
+ }
+
+ /**
+ * Format the channel array into an array of strings.
+ *
+ * @param array $channels
+ * @return array
+ */
+ protected function formatChannels(array $channels)
+ {
+ return array_map(function ($channel) {
+ return (string) $channel;
+ }, $channels);
+ }
+
+ /**
+ * Get the model binding registrar instance.
+ *
+ * @return \Illuminate\Contracts\Routing\BindingRegistrar
+ */
+ protected function binder()
+ {
+ if (! $this->bindingRegistrar) {
+ $this->bindingRegistrar = Container::getInstance()->bound(BindingRegistrar::class)
+ ? Container::getInstance()->make(BindingRegistrar::class) : null;
+ }
+
+ return $this->bindingRegistrar;
+ }
+
+ /**
+ * Normalize the given callback into a callable.
+ *
+ * @param mixed $callback
+ * @return callable
+ */
+ protected function normalizeChannelHandlerToCallable($callback)
+ {
+ return is_callable($callback) ? $callback : function (...$args) use ($callback) {
+ return Container::getInstance()
+ ->make($callback)
+ ->join(...$args);
+ };
+ }
+
+ /**
+ * Retrieve the authenticated user using the configured guard (if any).
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param string $channel
+ * @return mixed
+ */
+ protected function retrieveUser($request, $channel)
+ {
+ $options = $this->retrieveChannelOptions($channel);
+
+ $guards = $options['guards'] ?? null;
+
+ if (is_null($guards)) {
+ return $request->user();
+ }
+
+ foreach (Arr::wrap($guards) as $guard) {
+ if ($user = $request->user($guard)) {
+ return $user;
+ }
+ }
+ }
+
+ /**
+ * Retrieve options for a certain channel.
+ *
+ * @param string $channel
+ * @return array
+ */
+ protected function retrieveChannelOptions($channel)
+ {
+ foreach ($this->channelOptions as $pattern => $options) {
+ if (! $this->channelNameMatchesPattern($channel, $pattern)) {
+ continue;
+ }
+
+ return $options;
+ }
+
+ return [];
+ }
+
+ /**
+ * Check if the channel name from the request matches a pattern from registered channels.
+ *
+ * @param string $channel
+ * @param string $pattern
+ * @return bool
+ */
+ protected function channelNameMatchesPattern($channel, $pattern)
+ {
+ return Str::is(preg_replace('/\{(.*?)\}/', '*', $pattern), $channel);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/LogBroadcaster.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/LogBroadcaster.php
new file mode 100644
index 0000000..50877dc
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/LogBroadcaster.php
@@ -0,0 +1,54 @@
+logger = $logger;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function auth($request)
+ {
+ //
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validAuthenticationResponse($request, $result)
+ {
+ //
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function broadcast(array $channels, $event, array $payload = [])
+ {
+ $channels = implode(', ', $this->formatChannels($channels));
+
+ $payload = json_encode($payload, JSON_PRETTY_PRINT);
+
+ $this->logger->info('Broadcasting ['.$event.'] on channels ['.$channels.'] with payload:'.PHP_EOL.$payload);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/NullBroadcaster.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/NullBroadcaster.php
new file mode 100644
index 0000000..6205c90
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/NullBroadcaster.php
@@ -0,0 +1,30 @@
+pusher = $pusher;
+ }
+
+ /**
+ * Resolve the authenticated user payload for an incoming connection request.
+ *
+ * See: https://pusher.com/docs/channels/library_auth_reference/auth-signatures/#user-authentication
+ * See: https://pusher.com/docs/channels/server_api/authenticating-users/#response
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return array|null
+ */
+ public function resolveAuthenticatedUser($request)
+ {
+ if (! $user = parent::resolveAuthenticatedUser($request)) {
+ return;
+ }
+
+ if (method_exists($this->pusher, 'authenticateUser')) {
+ return $this->pusher->authenticateUser($request->socket_id, $user);
+ }
+
+ $settings = $this->pusher->getSettings();
+ $encodedUser = json_encode($user);
+ $decodedString = "{$request->socket_id}::user::{$encodedUser}";
+
+ $auth = $settings['auth_key'].':'.hash_hmac(
+ 'sha256', $decodedString, $settings['secret']
+ );
+
+ return [
+ 'auth' => $auth,
+ 'user_data' => $encodedUser,
+ ];
+ }
+
+ /**
+ * Authenticate the incoming request for a given channel.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return mixed
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+ */
+ public function auth($request)
+ {
+ $channelName = $this->normalizeChannelName($request->channel_name);
+
+ if (empty($request->channel_name) ||
+ ($this->isGuardedChannel($request->channel_name) &&
+ ! $this->retrieveUser($request, $channelName))) {
+ throw new AccessDeniedHttpException;
+ }
+
+ return parent::verifyUserCanAccessChannel(
+ $request, $channelName
+ );
+ }
+
+ /**
+ * Return the valid authentication response.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param mixed $result
+ * @return mixed
+ */
+ public function validAuthenticationResponse($request, $result)
+ {
+ if (str_starts_with($request->channel_name, 'private')) {
+ return $this->decodePusherResponse(
+ $request,
+ method_exists($this->pusher, 'authorizeChannel')
+ ? $this->pusher->authorizeChannel($request->channel_name, $request->socket_id)
+ : $this->pusher->socket_auth($request->channel_name, $request->socket_id)
+ );
+ }
+
+ $channelName = $this->normalizeChannelName($request->channel_name);
+
+ $user = $this->retrieveUser($request, $channelName);
+
+ $broadcastIdentifier = method_exists($user, 'getAuthIdentifierForBroadcasting')
+ ? $user->getAuthIdentifierForBroadcasting()
+ : $user->getAuthIdentifier();
+
+ return $this->decodePusherResponse(
+ $request,
+ method_exists($this->pusher, 'authorizePresenceChannel')
+ ? $this->pusher->authorizePresenceChannel($request->channel_name, $request->socket_id, $broadcastIdentifier, $result)
+ : $this->pusher->presence_auth($request->channel_name, $request->socket_id, $broadcastIdentifier, $result)
+ );
+ }
+
+ /**
+ * Decode the given Pusher response.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param mixed $response
+ * @return array
+ */
+ protected function decodePusherResponse($request, $response)
+ {
+ if (! $request->input('callback', false)) {
+ return json_decode($response, true);
+ }
+
+ return response()->json(json_decode($response, true))
+ ->withCallback($request->callback);
+ }
+
+ /**
+ * Broadcast the given event.
+ *
+ * @param array $channels
+ * @param string $event
+ * @param array $payload
+ * @return void
+ *
+ * @throws \Illuminate\Broadcasting\BroadcastException
+ */
+ public function broadcast(array $channels, $event, array $payload = [])
+ {
+ $socket = Arr::pull($payload, 'socket');
+
+ $parameters = $socket !== null ? ['socket_id' => $socket] : [];
+
+ $channels = Collection::make($this->formatChannels($channels));
+
+ try {
+ $channels->chunk(100)->each(function ($channels) use ($event, $payload, $parameters) {
+ $this->pusher->trigger($channels->toArray(), $event, $payload, $parameters);
+ });
+ } catch (ApiErrorException $e) {
+ throw new BroadcastException(
+ sprintf('Pusher error: %s.', $e->getMessage())
+ );
+ }
+ }
+
+ /**
+ * Get the Pusher SDK instance.
+ *
+ * @return \Pusher\Pusher
+ */
+ public function getPusher()
+ {
+ return $this->pusher;
+ }
+
+ /**
+ * Set the Pusher SDK instance.
+ *
+ * @param \Pusher\Pusher $pusher
+ * @return void
+ */
+ public function setPusher($pusher)
+ {
+ $this->pusher = $pusher;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php
new file mode 100644
index 0000000..5ae2e03
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/RedisBroadcaster.php
@@ -0,0 +1,169 @@
+redis = $redis;
+ $this->prefix = $prefix;
+ $this->connection = $connection;
+ }
+
+ /**
+ * Authenticate the incoming request for a given channel.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return mixed
+ *
+ * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+ */
+ public function auth($request)
+ {
+ $channelName = $this->normalizeChannelName(
+ str_replace($this->prefix, '', $request->channel_name)
+ );
+
+ if (empty($request->channel_name) ||
+ ($this->isGuardedChannel($request->channel_name) &&
+ ! $this->retrieveUser($request, $channelName))) {
+ throw new AccessDeniedHttpException;
+ }
+
+ return parent::verifyUserCanAccessChannel(
+ $request, $channelName
+ );
+ }
+
+ /**
+ * Return the valid authentication response.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param mixed $result
+ * @return mixed
+ */
+ public function validAuthenticationResponse($request, $result)
+ {
+ if (is_bool($result)) {
+ return json_encode($result);
+ }
+
+ $channelName = $this->normalizeChannelName($request->channel_name);
+
+ $user = $this->retrieveUser($request, $channelName);
+
+ $broadcastIdentifier = method_exists($user, 'getAuthIdentifierForBroadcasting')
+ ? $user->getAuthIdentifierForBroadcasting()
+ : $user->getAuthIdentifier();
+
+ return json_encode(['channel_data' => [
+ 'user_id' => $broadcastIdentifier,
+ 'user_info' => $result,
+ ]]);
+ }
+
+ /**
+ * Broadcast the given event.
+ *
+ * @param array $channels
+ * @param string $event
+ * @param array $payload
+ * @return void
+ *
+ * @throws \Illuminate\Broadcasting\BroadcastException
+ */
+ public function broadcast(array $channels, $event, array $payload = [])
+ {
+ if (empty($channels)) {
+ return;
+ }
+
+ $connection = $this->redis->connection($this->connection);
+
+ $payload = json_encode([
+ 'event' => $event,
+ 'data' => $payload,
+ 'socket' => Arr::pull($payload, 'socket'),
+ ]);
+
+ try {
+ $connection->eval(
+ $this->broadcastMultipleChannelsScript(),
+ 0, $payload, ...$this->formatChannels($channels)
+ );
+ } catch (ConnectionException|RedisException $e) {
+ throw new BroadcastException(
+ sprintf('Redis error: %s.', $e->getMessage())
+ );
+ }
+ }
+
+ /**
+ * Get the Lua script for broadcasting to multiple channels.
+ *
+ * ARGV[1] - The payload
+ * ARGV[2...] - The channels
+ *
+ * @return string
+ */
+ protected function broadcastMultipleChannelsScript()
+ {
+ return <<<'LUA'
+for i = 2, #ARGV do
+ redis.call('publish', ARGV[i], ARGV[1])
+end
+LUA;
+ }
+
+ /**
+ * Format the channel array into an array of strings.
+ *
+ * @param array $channels
+ * @return array
+ */
+ protected function formatChannels(array $channels)
+ {
+ return array_map(function ($channel) {
+ return $this->prefix.$channel;
+ }, parent::formatChannels($channels));
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/UsePusherChannelConventions.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/UsePusherChannelConventions.php
new file mode 100644
index 0000000..690cf3d
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/UsePusherChannelConventions.php
@@ -0,0 +1,36 @@
+name = $name instanceof HasBroadcastChannel ? $name->broadcastChannel() : $name;
+ }
+
+ /**
+ * Convert the channel instance to a string.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->name;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/EncryptedPrivateChannel.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/EncryptedPrivateChannel.php
new file mode 100644
index 0000000..76977c1
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/EncryptedPrivateChannel.php
@@ -0,0 +1,17 @@
+broadcastConnection = is_null($connection)
+ ? [null]
+ : Arr::wrap($connection);
+
+ return $this;
+ }
+
+ /**
+ * Get the broadcaster connections the event should be broadcast on.
+ *
+ * @return array
+ */
+ public function broadcastConnections()
+ {
+ return $this->broadcastConnection;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/InteractsWithSockets.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/InteractsWithSockets.php
new file mode 100644
index 0000000..6be0791
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/InteractsWithSockets.php
@@ -0,0 +1,39 @@
+socket = Broadcast::socket();
+
+ return $this;
+ }
+
+ /**
+ * Broadcast the event to everyone.
+ *
+ * @return $this
+ */
+ public function broadcastToEveryone()
+ {
+ $this->socket = null;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/LICENSE.md b/vendor/laravel/framework/src/Illuminate/Broadcasting/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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.
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/PendingBroadcast.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/PendingBroadcast.php
new file mode 100644
index 0000000..191b905
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/PendingBroadcast.php
@@ -0,0 +1,74 @@
+event = $event;
+ $this->events = $events;
+ }
+
+ /**
+ * Broadcast the event using a specific broadcaster.
+ *
+ * @param string|null $connection
+ * @return $this
+ */
+ public function via($connection = null)
+ {
+ if (method_exists($this->event, 'broadcastVia')) {
+ $this->event->broadcastVia($connection);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Broadcast the event to everyone except the current user.
+ *
+ * @return $this
+ */
+ public function toOthers()
+ {
+ if (method_exists($this->event, 'dontBroadcastToCurrentUser')) {
+ $this->event->dontBroadcastToCurrentUser();
+ }
+
+ return $this;
+ }
+
+ /**
+ * Handle the object's destruction.
+ *
+ * @return void
+ */
+ public function __destruct()
+ {
+ $this->events->dispatch($this->event);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/PresenceChannel.php b/vendor/laravel/framework/src/Illuminate/Broadcasting/PresenceChannel.php
new file mode 100644
index 0000000..22de12d
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/PresenceChannel.php
@@ -0,0 +1,17 @@
+broadcastChannel() : $name;
+
+ parent::__construct('private-'.$name);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Broadcasting/composer.json b/vendor/laravel/framework/src/Illuminate/Broadcasting/composer.json
new file mode 100644
index 0000000..f0736a3
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Broadcasting/composer.json
@@ -0,0 +1,44 @@
+{
+ "name": "illuminate/broadcasting",
+ "description": "The Illuminate Broadcasting package.",
+ "license": "MIT",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^8.0.2",
+ "ext-json": "*",
+ "psr/log": "^1.0|^2.0|^3.0",
+ "illuminate/bus": "^9.0",
+ "illuminate/collections": "^9.0",
+ "illuminate/contracts": "^9.0",
+ "illuminate/queue": "^9.0",
+ "illuminate/support": "^9.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Broadcasting\\": ""
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "suggest": {
+ "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
+ "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0)."
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "minimum-stability": "dev"
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/Batch.php b/vendor/laravel/framework/src/Illuminate/Bus/Batch.php
new file mode 100644
index 0000000..644bfbc
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/Batch.php
@@ -0,0 +1,480 @@
+queue = $queue;
+ $this->repository = $repository;
+ $this->id = $id;
+ $this->name = $name;
+ $this->totalJobs = $totalJobs;
+ $this->pendingJobs = $pendingJobs;
+ $this->failedJobs = $failedJobs;
+ $this->failedJobIds = $failedJobIds;
+ $this->options = $options;
+ $this->createdAt = $createdAt;
+ $this->cancelledAt = $cancelledAt;
+ $this->finishedAt = $finishedAt;
+ }
+
+ /**
+ * Get a fresh instance of the batch represented by this ID.
+ *
+ * @return self
+ */
+ public function fresh()
+ {
+ return $this->repository->find($this->id);
+ }
+
+ /**
+ * Add additional jobs to the batch.
+ *
+ * @param \Illuminate\Support\Enumerable|object|array $jobs
+ * @return self
+ */
+ public function add($jobs)
+ {
+ $count = 0;
+
+ $jobs = Collection::wrap($jobs)->map(function ($job) use (&$count) {
+ $job = $job instanceof Closure ? CallQueuedClosure::create($job) : $job;
+
+ if (is_array($job)) {
+ $count += count($job);
+
+ return with($this->prepareBatchedChain($job), function ($chain) {
+ return $chain->first()
+ ->allOnQueue($this->options['queue'] ?? null)
+ ->allOnConnection($this->options['connection'] ?? null)
+ ->chain($chain->slice(1)->values()->all());
+ });
+ } else {
+ $job->withBatchId($this->id);
+
+ $count++;
+ }
+
+ return $job;
+ });
+
+ $this->repository->transaction(function () use ($jobs, $count) {
+ $this->repository->incrementTotalJobs($this->id, $count);
+
+ $this->queue->connection($this->options['connection'] ?? null)->bulk(
+ $jobs->all(),
+ $data = '',
+ $this->options['queue'] ?? null
+ );
+ });
+
+ return $this->fresh();
+ }
+
+ /**
+ * Prepare a chain that exists within the jobs being added.
+ *
+ * @param array $chain
+ * @return \Illuminate\Support\Collection
+ */
+ protected function prepareBatchedChain(array $chain)
+ {
+ return collect($chain)->map(function ($job) {
+ $job = $job instanceof Closure ? CallQueuedClosure::create($job) : $job;
+
+ return $job->withBatchId($this->id);
+ });
+ }
+
+ /**
+ * Get the total number of jobs that have been processed by the batch thus far.
+ *
+ * @return int
+ */
+ public function processedJobs()
+ {
+ return $this->totalJobs - $this->pendingJobs;
+ }
+
+ /**
+ * Get the percentage of jobs that have been processed (between 0-100).
+ *
+ * @return int
+ */
+ public function progress()
+ {
+ return $this->totalJobs > 0 ? round(($this->processedJobs() / $this->totalJobs) * 100) : 0;
+ }
+
+ /**
+ * Record that a job within the batch finished successfully, executing any callbacks if necessary.
+ *
+ * @param string $jobId
+ * @return void
+ */
+ public function recordSuccessfulJob(string $jobId)
+ {
+ $counts = $this->decrementPendingJobs($jobId);
+
+ if ($counts->pendingJobs === 0) {
+ $this->repository->markAsFinished($this->id);
+ }
+
+ if ($counts->pendingJobs === 0 && $this->hasThenCallbacks()) {
+ $batch = $this->fresh();
+
+ collect($this->options['then'])->each(function ($handler) use ($batch) {
+ $this->invokeHandlerCallback($handler, $batch);
+ });
+ }
+
+ if ($counts->allJobsHaveRanExactlyOnce() && $this->hasFinallyCallbacks()) {
+ $batch = $this->fresh();
+
+ collect($this->options['finally'])->each(function ($handler) use ($batch) {
+ $this->invokeHandlerCallback($handler, $batch);
+ });
+ }
+ }
+
+ /**
+ * Decrement the pending jobs for the batch.
+ *
+ * @param string $jobId
+ * @return \Illuminate\Bus\UpdatedBatchJobCounts
+ */
+ public function decrementPendingJobs(string $jobId)
+ {
+ return $this->repository->decrementPendingJobs($this->id, $jobId);
+ }
+
+ /**
+ * Determine if the batch has finished executing.
+ *
+ * @return bool
+ */
+ public function finished()
+ {
+ return ! is_null($this->finishedAt);
+ }
+
+ /**
+ * Determine if the batch has "success" callbacks.
+ *
+ * @return bool
+ */
+ public function hasThenCallbacks()
+ {
+ return isset($this->options['then']) && ! empty($this->options['then']);
+ }
+
+ /**
+ * Determine if the batch allows jobs to fail without cancelling the batch.
+ *
+ * @return bool
+ */
+ public function allowsFailures()
+ {
+ return Arr::get($this->options, 'allowFailures', false) === true;
+ }
+
+ /**
+ * Determine if the batch has job failures.
+ *
+ * @return bool
+ */
+ public function hasFailures()
+ {
+ return $this->failedJobs > 0;
+ }
+
+ /**
+ * Record that a job within the batch failed to finish successfully, executing any callbacks if necessary.
+ *
+ * @param string $jobId
+ * @param \Throwable $e
+ * @return void
+ */
+ public function recordFailedJob(string $jobId, $e)
+ {
+ $counts = $this->incrementFailedJobs($jobId);
+
+ if ($counts->failedJobs === 1 && ! $this->allowsFailures()) {
+ $this->cancel();
+ }
+
+ if ($counts->failedJobs === 1 && $this->hasCatchCallbacks()) {
+ $batch = $this->fresh();
+
+ collect($this->options['catch'])->each(function ($handler) use ($batch, $e) {
+ $this->invokeHandlerCallback($handler, $batch, $e);
+ });
+ }
+
+ if ($counts->allJobsHaveRanExactlyOnce() && $this->hasFinallyCallbacks()) {
+ $batch = $this->fresh();
+
+ collect($this->options['finally'])->each(function ($handler) use ($batch, $e) {
+ $this->invokeHandlerCallback($handler, $batch, $e);
+ });
+ }
+ }
+
+ /**
+ * Increment the failed jobs for the batch.
+ *
+ * @param string $jobId
+ * @return \Illuminate\Bus\UpdatedBatchJobCounts
+ */
+ public function incrementFailedJobs(string $jobId)
+ {
+ return $this->repository->incrementFailedJobs($this->id, $jobId);
+ }
+
+ /**
+ * Determine if the batch has "catch" callbacks.
+ *
+ * @return bool
+ */
+ public function hasCatchCallbacks()
+ {
+ return isset($this->options['catch']) && ! empty($this->options['catch']);
+ }
+
+ /**
+ * Determine if the batch has "finally" callbacks.
+ *
+ * @return bool
+ */
+ public function hasFinallyCallbacks()
+ {
+ return isset($this->options['finally']) && ! empty($this->options['finally']);
+ }
+
+ /**
+ * Cancel the batch.
+ *
+ * @return void
+ */
+ public function cancel()
+ {
+ $this->repository->cancel($this->id);
+ }
+
+ /**
+ * Determine if the batch has been cancelled.
+ *
+ * @return bool
+ */
+ public function canceled()
+ {
+ return $this->cancelled();
+ }
+
+ /**
+ * Determine if the batch has been cancelled.
+ *
+ * @return bool
+ */
+ public function cancelled()
+ {
+ return ! is_null($this->cancelledAt);
+ }
+
+ /**
+ * Delete the batch from storage.
+ *
+ * @return void
+ */
+ public function delete()
+ {
+ $this->repository->delete($this->id);
+ }
+
+ /**
+ * Invoke a batch callback handler.
+ *
+ * @param callable $handler
+ * @param \Illuminate\Bus\Batch $batch
+ * @param \Throwable|null $e
+ * @return void
+ */
+ protected function invokeHandlerCallback($handler, Batch $batch, Throwable $e = null)
+ {
+ try {
+ return $handler($batch, $e);
+ } catch (Throwable $e) {
+ if (function_exists('report')) {
+ report($e);
+ }
+ }
+ }
+
+ /**
+ * Convert the batch to an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return [
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'totalJobs' => $this->totalJobs,
+ 'pendingJobs' => $this->pendingJobs,
+ 'processedJobs' => $this->processedJobs(),
+ 'progress' => $this->progress(),
+ 'failedJobs' => $this->failedJobs,
+ 'options' => $this->options,
+ 'createdAt' => $this->createdAt,
+ 'cancelledAt' => $this->cancelledAt,
+ 'finishedAt' => $this->finishedAt,
+ ];
+ }
+
+ /**
+ * Get the JSON serializable representation of the object.
+ *
+ * @return array
+ */
+ public function jsonSerialize(): array
+ {
+ return $this->toArray();
+ }
+
+ /**
+ * Dynamically access the batch's "options" via properties.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function __get($key)
+ {
+ return $this->options[$key] ?? null;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/BatchFactory.php b/vendor/laravel/framework/src/Illuminate/Bus/BatchFactory.php
new file mode 100644
index 0000000..2c3a4e9
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/BatchFactory.php
@@ -0,0 +1,58 @@
+queue = $queue;
+ }
+
+ /**
+ * Create a new batch instance.
+ *
+ * @param \Illuminate\Bus\BatchRepository $repository
+ * @param string $id
+ * @param string $name
+ * @param int $totalJobs
+ * @param int $pendingJobs
+ * @param int $failedJobs
+ * @param array $failedJobIds
+ * @param array $options
+ * @param \Carbon\CarbonImmutable $createdAt
+ * @param \Carbon\CarbonImmutable|null $cancelledAt
+ * @param \Carbon\CarbonImmutable|null $finishedAt
+ * @return \Illuminate\Bus\Batch
+ */
+ public function make(BatchRepository $repository,
+ string $id,
+ string $name,
+ int $totalJobs,
+ int $pendingJobs,
+ int $failedJobs,
+ array $failedJobIds,
+ array $options,
+ CarbonImmutable $createdAt,
+ ?CarbonImmutable $cancelledAt,
+ ?CarbonImmutable $finishedAt)
+ {
+ return new Batch($this->queue, $repository, $id, $name, $totalJobs, $pendingJobs, $failedJobs, $failedJobIds, $options, $createdAt, $cancelledAt, $finishedAt);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/BatchRepository.php b/vendor/laravel/framework/src/Illuminate/Bus/BatchRepository.php
new file mode 100644
index 0000000..098ccef
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/BatchRepository.php
@@ -0,0 +1,92 @@
+batchId) {
+ return Container::getInstance()->make(BatchRepository::class)->find($this->batchId);
+ }
+ }
+
+ /**
+ * Determine if the batch is still active and processing.
+ *
+ * @return bool
+ */
+ public function batching()
+ {
+ $batch = $this->batch();
+
+ return $batch && ! $batch->cancelled();
+ }
+
+ /**
+ * Set the batch ID on the job.
+ *
+ * @param string $batchId
+ * @return $this
+ */
+ public function withBatchId(string $batchId)
+ {
+ $this->batchId = $batchId;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/BusServiceProvider.php b/vendor/laravel/framework/src/Illuminate/Bus/BusServiceProvider.php
new file mode 100644
index 0000000..ff3eef8
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/BusServiceProvider.php
@@ -0,0 +1,69 @@
+app->singleton(Dispatcher::class, function ($app) {
+ return new Dispatcher($app, function ($connection = null) use ($app) {
+ return $app[QueueFactoryContract::class]->connection($connection);
+ });
+ });
+
+ $this->registerBatchServices();
+
+ $this->app->alias(
+ Dispatcher::class, DispatcherContract::class
+ );
+
+ $this->app->alias(
+ Dispatcher::class, QueueingDispatcherContract::class
+ );
+ }
+
+ /**
+ * Register the batch handling services.
+ *
+ * @return void
+ */
+ protected function registerBatchServices()
+ {
+ $this->app->singleton(BatchRepository::class, DatabaseBatchRepository::class);
+
+ $this->app->singleton(DatabaseBatchRepository::class, function ($app) {
+ return new DatabaseBatchRepository(
+ $app->make(BatchFactory::class),
+ $app->make('db')->connection($app->config->get('queue.batching.database')),
+ $app->config->get('queue.batching.table', 'job_batches')
+ );
+ });
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides()
+ {
+ return [
+ Dispatcher::class,
+ DispatcherContract::class,
+ QueueingDispatcherContract::class,
+ BatchRepository::class,
+ ];
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/DatabaseBatchRepository.php b/vendor/laravel/framework/src/Illuminate/Bus/DatabaseBatchRepository.php
new file mode 100644
index 0000000..de91c88
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/DatabaseBatchRepository.php
@@ -0,0 +1,343 @@
+factory = $factory;
+ $this->connection = $connection;
+ $this->table = $table;
+ }
+
+ /**
+ * Retrieve a list of batches.
+ *
+ * @param int $limit
+ * @param mixed $before
+ * @return \Illuminate\Bus\Batch[]
+ */
+ public function get($limit = 50, $before = null)
+ {
+ return $this->connection->table($this->table)
+ ->orderByDesc('id')
+ ->take($limit)
+ ->when($before, fn ($q) => $q->where('id', '<', $before))
+ ->get()
+ ->map(function ($batch) {
+ return $this->toBatch($batch);
+ })
+ ->all();
+ }
+
+ /**
+ * Retrieve information about an existing batch.
+ *
+ * @param string $batchId
+ * @return \Illuminate\Bus\Batch|null
+ */
+ public function find(string $batchId)
+ {
+ $batch = $this->connection->table($this->table)
+ ->where('id', $batchId)
+ ->first();
+
+ if ($batch) {
+ return $this->toBatch($batch);
+ }
+ }
+
+ /**
+ * Store a new pending batch.
+ *
+ * @param \Illuminate\Bus\PendingBatch $batch
+ * @return \Illuminate\Bus\Batch
+ */
+ public function store(PendingBatch $batch)
+ {
+ $id = (string) Str::orderedUuid();
+
+ $this->connection->table($this->table)->insert([
+ 'id' => $id,
+ 'name' => $batch->name,
+ 'total_jobs' => 0,
+ 'pending_jobs' => 0,
+ 'failed_jobs' => 0,
+ 'failed_job_ids' => '[]',
+ 'options' => $this->serialize($batch->options),
+ 'created_at' => time(),
+ 'cancelled_at' => null,
+ 'finished_at' => null,
+ ]);
+
+ return $this->find($id);
+ }
+
+ /**
+ * Increment the total number of jobs within the batch.
+ *
+ * @param string $batchId
+ * @param int $amount
+ * @return void
+ */
+ public function incrementTotalJobs(string $batchId, int $amount)
+ {
+ $this->connection->table($this->table)->where('id', $batchId)->update([
+ 'total_jobs' => new Expression('total_jobs + '.$amount),
+ 'pending_jobs' => new Expression('pending_jobs + '.$amount),
+ 'finished_at' => null,
+ ]);
+ }
+
+ /**
+ * Decrement the total number of pending jobs for the batch.
+ *
+ * @param string $batchId
+ * @param string $jobId
+ * @return \Illuminate\Bus\UpdatedBatchJobCounts
+ */
+ public function decrementPendingJobs(string $batchId, string $jobId)
+ {
+ $values = $this->updateAtomicValues($batchId, function ($batch) use ($jobId) {
+ return [
+ 'pending_jobs' => $batch->pending_jobs - 1,
+ 'failed_jobs' => $batch->failed_jobs,
+ 'failed_job_ids' => json_encode(array_values(array_diff(json_decode($batch->failed_job_ids, true), [$jobId]))),
+ ];
+ });
+
+ return new UpdatedBatchJobCounts(
+ $values['pending_jobs'],
+ $values['failed_jobs']
+ );
+ }
+
+ /**
+ * Increment the total number of failed jobs for the batch.
+ *
+ * @param string $batchId
+ * @param string $jobId
+ * @return \Illuminate\Bus\UpdatedBatchJobCounts
+ */
+ public function incrementFailedJobs(string $batchId, string $jobId)
+ {
+ $values = $this->updateAtomicValues($batchId, function ($batch) use ($jobId) {
+ return [
+ 'pending_jobs' => $batch->pending_jobs,
+ 'failed_jobs' => $batch->failed_jobs + 1,
+ 'failed_job_ids' => json_encode(array_values(array_unique(array_merge(json_decode($batch->failed_job_ids, true), [$jobId])))),
+ ];
+ });
+
+ return new UpdatedBatchJobCounts(
+ $values['pending_jobs'],
+ $values['failed_jobs']
+ );
+ }
+
+ /**
+ * Update an atomic value within the batch.
+ *
+ * @param string $batchId
+ * @param \Closure $callback
+ * @return int|null
+ */
+ protected function updateAtomicValues(string $batchId, Closure $callback)
+ {
+ return $this->connection->transaction(function () use ($batchId, $callback) {
+ $batch = $this->connection->table($this->table)->where('id', $batchId)
+ ->lockForUpdate()
+ ->first();
+
+ return is_null($batch) ? [] : tap($callback($batch), function ($values) use ($batchId) {
+ $this->connection->table($this->table)->where('id', $batchId)->update($values);
+ });
+ });
+ }
+
+ /**
+ * Mark the batch that has the given ID as finished.
+ *
+ * @param string $batchId
+ * @return void
+ */
+ public function markAsFinished(string $batchId)
+ {
+ $this->connection->table($this->table)->where('id', $batchId)->update([
+ 'finished_at' => time(),
+ ]);
+ }
+
+ /**
+ * Cancel the batch that has the given ID.
+ *
+ * @param string $batchId
+ * @return void
+ */
+ public function cancel(string $batchId)
+ {
+ $this->connection->table($this->table)->where('id', $batchId)->update([
+ 'cancelled_at' => time(),
+ 'finished_at' => time(),
+ ]);
+ }
+
+ /**
+ * Delete the batch that has the given ID.
+ *
+ * @param string $batchId
+ * @return void
+ */
+ public function delete(string $batchId)
+ {
+ $this->connection->table($this->table)->where('id', $batchId)->delete();
+ }
+
+ /**
+ * Prune all of the entries older than the given date.
+ *
+ * @param \DateTimeInterface $before
+ * @return int
+ */
+ public function prune(DateTimeInterface $before)
+ {
+ $query = $this->connection->table($this->table)
+ ->whereNotNull('finished_at')
+ ->where('finished_at', '<', $before->getTimestamp());
+
+ $totalDeleted = 0;
+
+ do {
+ $deleted = $query->take(1000)->delete();
+
+ $totalDeleted += $deleted;
+ } while ($deleted !== 0);
+
+ return $totalDeleted;
+ }
+
+ /**
+ * Prune all of the unfinished entries older than the given date.
+ *
+ * @param \DateTimeInterface $before
+ * @return int
+ */
+ public function pruneUnfinished(DateTimeInterface $before)
+ {
+ $query = $this->connection->table($this->table)
+ ->whereNull('finished_at')
+ ->where('created_at', '<', $before->getTimestamp());
+
+ $totalDeleted = 0;
+
+ do {
+ $deleted = $query->take(1000)->delete();
+
+ $totalDeleted += $deleted;
+ } while ($deleted !== 0);
+
+ return $totalDeleted;
+ }
+
+ /**
+ * Execute the given Closure within a storage specific transaction.
+ *
+ * @param \Closure $callback
+ * @return mixed
+ */
+ public function transaction(Closure $callback)
+ {
+ return $this->connection->transaction(fn () => $callback());
+ }
+
+ /**
+ * Serialize the given value.
+ *
+ * @param mixed $value
+ * @return string
+ */
+ protected function serialize($value)
+ {
+ $serialized = serialize($value);
+
+ return $this->connection instanceof PostgresConnection
+ ? base64_encode($serialized)
+ : $serialized;
+ }
+
+ /**
+ * Unserialize the given value.
+ *
+ * @param string $serialized
+ * @return mixed
+ */
+ protected function unserialize($serialized)
+ {
+ if ($this->connection instanceof PostgresConnection &&
+ ! Str::contains($serialized, [':', ';'])) {
+ $serialized = base64_decode($serialized);
+ }
+
+ return unserialize($serialized);
+ }
+
+ /**
+ * Convert the given raw batch to a Batch object.
+ *
+ * @param object $batch
+ * @return \Illuminate\Bus\Batch
+ */
+ protected function toBatch($batch)
+ {
+ return $this->factory->make(
+ $this,
+ $batch->id,
+ $batch->name,
+ (int) $batch->total_jobs,
+ (int) $batch->pending_jobs,
+ (int) $batch->failed_jobs,
+ json_decode($batch->failed_job_ids, true),
+ $this->unserialize($batch->options),
+ CarbonImmutable::createFromTimestamp($batch->created_at),
+ $batch->cancelled_at ? CarbonImmutable::createFromTimestamp($batch->cancelled_at) : $batch->cancelled_at,
+ $batch->finished_at ? CarbonImmutable::createFromTimestamp($batch->finished_at) : $batch->finished_at
+ );
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php b/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php
new file mode 100644
index 0000000..4dc390e
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php
@@ -0,0 +1,295 @@
+container = $container;
+ $this->queueResolver = $queueResolver;
+ $this->pipeline = new Pipeline($container);
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler.
+ *
+ * @param mixed $command
+ * @return mixed
+ */
+ public function dispatch($command)
+ {
+ return $this->queueResolver && $this->commandShouldBeQueued($command)
+ ? $this->dispatchToQueue($command)
+ : $this->dispatchNow($command);
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler in the current process.
+ *
+ * Queueable jobs will be dispatched to the "sync" queue.
+ *
+ * @param mixed $command
+ * @param mixed $handler
+ * @return mixed
+ */
+ public function dispatchSync($command, $handler = null)
+ {
+ if ($this->queueResolver &&
+ $this->commandShouldBeQueued($command) &&
+ method_exists($command, 'onConnection')) {
+ return $this->dispatchToQueue($command->onConnection('sync'));
+ }
+
+ return $this->dispatchNow($command, $handler);
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler in the current process without using the synchronous queue.
+ *
+ * @param mixed $command
+ * @param mixed $handler
+ * @return mixed
+ */
+ public function dispatchNow($command, $handler = null)
+ {
+ $uses = class_uses_recursive($command);
+
+ if (in_array(InteractsWithQueue::class, $uses) &&
+ in_array(Queueable::class, $uses) &&
+ ! $command->job) {
+ $command->setJob(new SyncJob($this->container, json_encode([]), 'sync', 'sync'));
+ }
+
+ if ($handler || $handler = $this->getCommandHandler($command)) {
+ $callback = function ($command) use ($handler) {
+ $method = method_exists($handler, 'handle') ? 'handle' : '__invoke';
+
+ return $handler->{$method}($command);
+ };
+ } else {
+ $callback = function ($command) {
+ $method = method_exists($command, 'handle') ? 'handle' : '__invoke';
+
+ return $this->container->call([$command, $method]);
+ };
+ }
+
+ return $this->pipeline->send($command)->through($this->pipes)->then($callback);
+ }
+
+ /**
+ * Attempt to find the batch with the given ID.
+ *
+ * @param string $batchId
+ * @return \Illuminate\Bus\Batch|null
+ */
+ public function findBatch(string $batchId)
+ {
+ return $this->container->make(BatchRepository::class)->find($batchId);
+ }
+
+ /**
+ * Create a new batch of queueable jobs.
+ *
+ * @param \Illuminate\Support\Collection|array|mixed $jobs
+ * @return \Illuminate\Bus\PendingBatch
+ */
+ public function batch($jobs)
+ {
+ return new PendingBatch($this->container, Collection::wrap($jobs));
+ }
+
+ /**
+ * Create a new chain of queueable jobs.
+ *
+ * @param \Illuminate\Support\Collection|array $jobs
+ * @return \Illuminate\Foundation\Bus\PendingChain
+ */
+ public function chain($jobs)
+ {
+ $jobs = Collection::wrap($jobs);
+
+ return new PendingChain($jobs->shift(), $jobs->toArray());
+ }
+
+ /**
+ * Determine if the given command has a handler.
+ *
+ * @param mixed $command
+ * @return bool
+ */
+ public function hasCommandHandler($command)
+ {
+ return array_key_exists(get_class($command), $this->handlers);
+ }
+
+ /**
+ * Retrieve the handler for a command.
+ *
+ * @param mixed $command
+ * @return bool|mixed
+ */
+ public function getCommandHandler($command)
+ {
+ if ($this->hasCommandHandler($command)) {
+ return $this->container->make($this->handlers[get_class($command)]);
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine if the given command should be queued.
+ *
+ * @param mixed $command
+ * @return bool
+ */
+ protected function commandShouldBeQueued($command)
+ {
+ return $command instanceof ShouldQueue;
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler behind a queue.
+ *
+ * @param mixed $command
+ * @return mixed
+ *
+ * @throws \RuntimeException
+ */
+ public function dispatchToQueue($command)
+ {
+ $connection = $command->connection ?? null;
+
+ $queue = call_user_func($this->queueResolver, $connection);
+
+ if (! $queue instanceof Queue) {
+ throw new RuntimeException('Queue resolver did not return a Queue implementation.');
+ }
+
+ if (method_exists($command, 'queue')) {
+ return $command->queue($queue, $command);
+ }
+
+ return $this->pushCommandToQueue($queue, $command);
+ }
+
+ /**
+ * Push the command onto the given queue instance.
+ *
+ * @param \Illuminate\Contracts\Queue\Queue $queue
+ * @param mixed $command
+ * @return mixed
+ */
+ protected function pushCommandToQueue($queue, $command)
+ {
+ if (isset($command->queue, $command->delay)) {
+ return $queue->laterOn($command->queue, $command->delay, $command);
+ }
+
+ if (isset($command->queue)) {
+ return $queue->pushOn($command->queue, $command);
+ }
+
+ if (isset($command->delay)) {
+ return $queue->later($command->delay, $command);
+ }
+
+ return $queue->push($command);
+ }
+
+ /**
+ * Dispatch a command to its appropriate handler after the current process.
+ *
+ * @param mixed $command
+ * @param mixed $handler
+ * @return void
+ */
+ public function dispatchAfterResponse($command, $handler = null)
+ {
+ $this->container->terminating(function () use ($command, $handler) {
+ $this->dispatchNow($command, $handler);
+ });
+ }
+
+ /**
+ * Set the pipes through which commands should be piped before dispatching.
+ *
+ * @param array $pipes
+ * @return $this
+ */
+ public function pipeThrough(array $pipes)
+ {
+ $this->pipes = $pipes;
+
+ return $this;
+ }
+
+ /**
+ * Map a command to a handler.
+ *
+ * @param array $map
+ * @return $this
+ */
+ public function map(array $map)
+ {
+ $this->handlers = array_merge($this->handlers, $map);
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/Events/BatchDispatched.php b/vendor/laravel/framework/src/Illuminate/Bus/Events/BatchDispatched.php
new file mode 100644
index 0000000..b9a161a
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/Events/BatchDispatched.php
@@ -0,0 +1,26 @@
+batch = $batch;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/LICENSE.md b/vendor/laravel/framework/src/Illuminate/Bus/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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.
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/PendingBatch.php b/vendor/laravel/framework/src/Illuminate/Bus/PendingBatch.php
new file mode 100644
index 0000000..6cd5183
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/PendingBatch.php
@@ -0,0 +1,319 @@
+container = $container;
+ $this->jobs = $jobs;
+ }
+
+ /**
+ * Add jobs to the batch.
+ *
+ * @param iterable|object|array $jobs
+ * @return $this
+ */
+ public function add($jobs)
+ {
+ $jobs = is_iterable($jobs) ? $jobs : Arr::wrap($jobs);
+
+ foreach ($jobs as $job) {
+ $this->jobs->push($job);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add a callback to be executed after all jobs in the batch have executed successfully.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function then($callback)
+ {
+ $this->options['then'][] = $callback instanceof Closure
+ ? new SerializableClosure($callback)
+ : $callback;
+
+ return $this;
+ }
+
+ /**
+ * Get the "then" callbacks that have been registered with the pending batch.
+ *
+ * @return array
+ */
+ public function thenCallbacks()
+ {
+ return $this->options['then'] ?? [];
+ }
+
+ /**
+ * Add a callback to be executed after the first failing job in the batch.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function catch($callback)
+ {
+ $this->options['catch'][] = $callback instanceof Closure
+ ? new SerializableClosure($callback)
+ : $callback;
+
+ return $this;
+ }
+
+ /**
+ * Get the "catch" callbacks that have been registered with the pending batch.
+ *
+ * @return array
+ */
+ public function catchCallbacks()
+ {
+ return $this->options['catch'] ?? [];
+ }
+
+ /**
+ * Add a callback to be executed after the batch has finished executing.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function finally($callback)
+ {
+ $this->options['finally'][] = $callback instanceof Closure
+ ? new SerializableClosure($callback)
+ : $callback;
+
+ return $this;
+ }
+
+ /**
+ * Get the "finally" callbacks that have been registered with the pending batch.
+ *
+ * @return array
+ */
+ public function finallyCallbacks()
+ {
+ return $this->options['finally'] ?? [];
+ }
+
+ /**
+ * Indicate that the batch should not be cancelled when a job within the batch fails.
+ *
+ * @param bool $allowFailures
+ * @return $this
+ */
+ public function allowFailures($allowFailures = true)
+ {
+ $this->options['allowFailures'] = $allowFailures;
+
+ return $this;
+ }
+
+ /**
+ * Determine if the pending batch allows jobs to fail without cancelling the batch.
+ *
+ * @return bool
+ */
+ public function allowsFailures()
+ {
+ return Arr::get($this->options, 'allowFailures', false) === true;
+ }
+
+ /**
+ * Set the name for the batch.
+ *
+ * @param string $name
+ * @return $this
+ */
+ public function name(string $name)
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * Specify the queue connection that the batched jobs should run on.
+ *
+ * @param string $connection
+ * @return $this
+ */
+ public function onConnection(string $connection)
+ {
+ $this->options['connection'] = $connection;
+
+ return $this;
+ }
+
+ /**
+ * Get the connection used by the pending batch.
+ *
+ * @return string|null
+ */
+ public function connection()
+ {
+ return $this->options['connection'] ?? null;
+ }
+
+ /**
+ * Specify the queue that the batched jobs should run on.
+ *
+ * @param string $queue
+ * @return $this
+ */
+ public function onQueue(string $queue)
+ {
+ $this->options['queue'] = $queue;
+
+ return $this;
+ }
+
+ /**
+ * Get the queue used by the pending batch.
+ *
+ * @return string|null
+ */
+ public function queue()
+ {
+ return $this->options['queue'] ?? null;
+ }
+
+ /**
+ * Add additional data into the batch's options array.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return $this
+ */
+ public function withOption(string $key, $value)
+ {
+ $this->options[$key] = $value;
+
+ return $this;
+ }
+
+ /**
+ * Dispatch the batch.
+ *
+ * @return \Illuminate\Bus\Batch
+ *
+ * @throws \Throwable
+ */
+ public function dispatch()
+ {
+ $repository = $this->container->make(BatchRepository::class);
+
+ try {
+ $batch = $repository->store($this);
+
+ $batch = $batch->add($this->jobs);
+ } catch (Throwable $e) {
+ if (isset($batch)) {
+ $repository->delete($batch->id);
+ }
+
+ throw $e;
+ }
+
+ $this->container->make(EventDispatcher::class)->dispatch(
+ new BatchDispatched($batch)
+ );
+
+ return $batch;
+ }
+
+ /**
+ * Dispatch the batch after the response is sent to the browser.
+ *
+ * @return \Illuminate\Bus\Batch
+ */
+ public function dispatchAfterResponse()
+ {
+ $repository = $this->container->make(BatchRepository::class);
+
+ $batch = $repository->store($this);
+
+ if ($batch) {
+ $this->container->terminating(function () use ($batch) {
+ $this->dispatchExistingBatch($batch);
+ });
+ }
+
+ return $batch;
+ }
+
+ /**
+ * Dispatch an existing batch.
+ *
+ * @param \Illuminate\Bus\Batch $batch
+ * @return void
+ *
+ * @throws \Throwable
+ */
+ protected function dispatchExistingBatch($batch)
+ {
+ try {
+ $batch = $batch->add($this->jobs);
+ } catch (Throwable $e) {
+ if (isset($batch)) {
+ $batch->delete();
+ }
+
+ throw $e;
+ }
+
+ $this->container->make(EventDispatcher::class)->dispatch(
+ new BatchDispatched($batch)
+ );
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/PrunableBatchRepository.php b/vendor/laravel/framework/src/Illuminate/Bus/PrunableBatchRepository.php
new file mode 100644
index 0000000..3f97255
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/PrunableBatchRepository.php
@@ -0,0 +1,16 @@
+connection = $connection;
+
+ return $this;
+ }
+
+ /**
+ * Set the desired queue for the job.
+ *
+ * @param string|null $queue
+ * @return $this
+ */
+ public function onQueue($queue)
+ {
+ $this->queue = $queue;
+
+ return $this;
+ }
+
+ /**
+ * Set the desired connection for the chain.
+ *
+ * @param string|null $connection
+ * @return $this
+ */
+ public function allOnConnection($connection)
+ {
+ $this->chainConnection = $connection;
+ $this->connection = $connection;
+
+ return $this;
+ }
+
+ /**
+ * Set the desired queue for the chain.
+ *
+ * @param string|null $queue
+ * @return $this
+ */
+ public function allOnQueue($queue)
+ {
+ $this->chainQueue = $queue;
+ $this->queue = $queue;
+
+ return $this;
+ }
+
+ /**
+ * Set the desired delay in seconds for the job.
+ *
+ * @param \DateTimeInterface|\DateInterval|array|int|null $delay
+ * @return $this
+ */
+ public function delay($delay)
+ {
+ $this->delay = $delay;
+
+ return $this;
+ }
+
+ /**
+ * Indicate that the job should be dispatched after all database transactions have committed.
+ *
+ * @return $this
+ */
+ public function afterCommit()
+ {
+ $this->afterCommit = true;
+
+ return $this;
+ }
+
+ /**
+ * Indicate that the job should not wait until database transactions have been committed before dispatching.
+ *
+ * @return $this
+ */
+ public function beforeCommit()
+ {
+ $this->afterCommit = false;
+
+ return $this;
+ }
+
+ /**
+ * Specify the middleware the job should be dispatched through.
+ *
+ * @param array|object $middleware
+ * @return $this
+ */
+ public function through($middleware)
+ {
+ $this->middleware = Arr::wrap($middleware);
+
+ return $this;
+ }
+
+ /**
+ * Set the jobs that should run if this job is successful.
+ *
+ * @param array $chain
+ * @return $this
+ */
+ public function chain($chain)
+ {
+ $this->chained = collect($chain)->map(function ($job) {
+ return $this->serializeJob($job);
+ })->all();
+
+ return $this;
+ }
+
+ /**
+ * Prepend a job to the current chain so that it is run after the currently running job.
+ *
+ * @param mixed $job
+ * @return $this
+ */
+ public function prependToChain($job)
+ {
+ $this->chained = Arr::prepend($this->chained, $this->serializeJob($job));
+
+ return $this;
+ }
+
+ /**
+ * Append a job to the end of the current chain.
+ *
+ * @param mixed $job
+ * @return $this
+ */
+ public function appendToChain($job)
+ {
+ $this->chained = array_merge($this->chained, [$this->serializeJob($job)]);
+
+ return $this;
+ }
+
+ /**
+ * Serialize a job for queuing.
+ *
+ * @param mixed $job
+ * @return string
+ *
+ * @throws \RuntimeException
+ */
+ protected function serializeJob($job)
+ {
+ if ($job instanceof Closure) {
+ if (! class_exists(CallQueuedClosure::class)) {
+ throw new RuntimeException(
+ 'To enable support for closure jobs, please install the illuminate/queue package.'
+ );
+ }
+
+ $job = CallQueuedClosure::create($job);
+ }
+
+ return serialize($job);
+ }
+
+ /**
+ * Dispatch the next job on the chain.
+ *
+ * @return void
+ */
+ public function dispatchNextJobInChain()
+ {
+ if (! empty($this->chained)) {
+ dispatch(tap(unserialize(array_shift($this->chained)), function ($next) {
+ $next->chained = $this->chained;
+
+ $next->onConnection($next->connection ?: $this->chainConnection);
+ $next->onQueue($next->queue ?: $this->chainQueue);
+
+ $next->chainConnection = $this->chainConnection;
+ $next->chainQueue = $this->chainQueue;
+ $next->chainCatchCallbacks = $this->chainCatchCallbacks;
+ }));
+ }
+ }
+
+ /**
+ * Invoke all of the chain's failed job callbacks.
+ *
+ * @param \Throwable $e
+ * @return void
+ */
+ public function invokeChainCatchCallbacks($e)
+ {
+ collect($this->chainCatchCallbacks)->each(function ($callback) use ($e) {
+ $callback($e);
+ });
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/UniqueLock.php b/vendor/laravel/framework/src/Illuminate/Bus/UniqueLock.php
new file mode 100644
index 0000000..d1bd774
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/UniqueLock.php
@@ -0,0 +1,52 @@
+cache = $cache;
+ }
+
+ /**
+ * Attempt to acquire a lock for the given job.
+ *
+ * @param mixed $job
+ * @return bool
+ */
+ public function acquire($job)
+ {
+ $uniqueId = method_exists($job, 'uniqueId')
+ ? $job->uniqueId()
+ : ($job->uniqueId ?? '');
+
+ $uniqueFor = method_exists($job, 'uniqueFor')
+ ? $job->uniqueFor()
+ : ($job->uniqueFor ?? 0);
+
+ $cache = method_exists($job, 'uniqueVia')
+ ? $job->uniqueVia()
+ : $this->cache;
+
+ return (bool) $cache->lock(
+ $key = 'laravel_unique_job:'.get_class($job).$uniqueId,
+ $uniqueFor
+ )->get();
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/UpdatedBatchJobCounts.php b/vendor/laravel/framework/src/Illuminate/Bus/UpdatedBatchJobCounts.php
new file mode 100644
index 0000000..83d33a4
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/UpdatedBatchJobCounts.php
@@ -0,0 +1,43 @@
+pendingJobs = $pendingJobs;
+ $this->failedJobs = $failedJobs;
+ }
+
+ /**
+ * Determine if all jobs have run exactly once.
+ *
+ * @return bool
+ */
+ public function allJobsHaveRanExactlyOnce()
+ {
+ return ($this->pendingJobs - $this->failedJobs) === 0;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Bus/composer.json b/vendor/laravel/framework/src/Illuminate/Bus/composer.json
new file mode 100644
index 0000000..44e795a
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Bus/composer.json
@@ -0,0 +1,40 @@
+{
+ "name": "illuminate/bus",
+ "description": "The Illuminate Bus package.",
+ "license": "MIT",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^8.0.2",
+ "illuminate/collections": "^9.0",
+ "illuminate/contracts": "^9.0",
+ "illuminate/pipeline": "^9.0",
+ "illuminate/support": "^9.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Bus\\": ""
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "suggest": {
+ "illuminate/queue": "Required to use closures when chaining jobs (^7.0)."
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "minimum-stability": "dev"
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/ApcStore.php b/vendor/laravel/framework/src/Illuminate/Cache/ApcStore.php
new file mode 100755
index 0000000..90132c1
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/ApcStore.php
@@ -0,0 +1,130 @@
+apc = $apc;
+ $this->prefix = $prefix;
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param string|array $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ $value = $this->apc->get($this->prefix.$key);
+
+ if ($value !== false) {
+ return $value;
+ }
+ }
+
+ /**
+ * Store an item in the cache for a given number of seconds.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ return $this->apc->put($this->prefix.$key, $value, $seconds);
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function increment($key, $value = 1)
+ {
+ return $this->apc->increment($this->prefix.$key, $value);
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->apc->decrement($this->prefix.$key, $value);
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ return $this->put($key, $value, 0);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ return $this->apc->delete($this->prefix.$key);
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ return $this->apc->flush();
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return $this->prefix;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/ApcWrapper.php b/vendor/laravel/framework/src/Illuminate/Cache/ApcWrapper.php
new file mode 100755
index 0000000..6c129c6
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/ApcWrapper.php
@@ -0,0 +1,92 @@
+apcu = function_exists('apcu_fetch');
+ }
+
+ /**
+ * Get an item from the cache.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ return $this->apcu ? apcu_fetch($key) : apc_fetch($key);
+ }
+
+ /**
+ * Store an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return array|bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ return $this->apcu ? apcu_store($key, $value, $seconds) : apc_store($key, $value, $seconds);
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function increment($key, $value)
+ {
+ return $this->apcu ? apcu_inc($key, $value) : apc_inc($key, $value);
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function decrement($key, $value)
+ {
+ return $this->apcu ? apcu_dec($key, $value) : apc_dec($key, $value);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function delete($key)
+ {
+ return $this->apcu ? apcu_delete($key) : apc_delete($key);
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ return $this->apcu ? apcu_clear_cache() : apc_clear_cache('user');
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/ArrayLock.php b/vendor/laravel/framework/src/Illuminate/Cache/ArrayLock.php
new file mode 100644
index 0000000..4c20783
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/ArrayLock.php
@@ -0,0 +1,102 @@
+store = $store;
+ }
+
+ /**
+ * Attempt to acquire the lock.
+ *
+ * @return bool
+ */
+ public function acquire()
+ {
+ $expiration = $this->store->locks[$this->name]['expiresAt'] ?? Carbon::now()->addSecond();
+
+ if ($this->exists() && $expiration->isFuture()) {
+ return false;
+ }
+
+ $this->store->locks[$this->name] = [
+ 'owner' => $this->owner,
+ 'expiresAt' => $this->seconds === 0 ? null : Carbon::now()->addSeconds($this->seconds),
+ ];
+
+ return true;
+ }
+
+ /**
+ * Determine if the current lock exists.
+ *
+ * @return bool
+ */
+ protected function exists()
+ {
+ return isset($this->store->locks[$this->name]);
+ }
+
+ /**
+ * Release the lock.
+ *
+ * @return bool
+ */
+ public function release()
+ {
+ if (! $this->exists()) {
+ return false;
+ }
+
+ if (! $this->isOwnedByCurrentProcess()) {
+ return false;
+ }
+
+ $this->forceRelease();
+
+ return true;
+ }
+
+ /**
+ * Returns the owner value written into the driver for this lock.
+ *
+ * @return string
+ */
+ protected function getCurrentOwner()
+ {
+ return $this->store->locks[$this->name]['owner'];
+ }
+
+ /**
+ * Releases this lock in disregard of ownership.
+ *
+ * @return void
+ */
+ public function forceRelease()
+ {
+ unset($this->store->locks[$this->name]);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/ArrayStore.php b/vendor/laravel/framework/src/Illuminate/Cache/ArrayStore.php
new file mode 100644
index 0000000..22b42ba
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/ArrayStore.php
@@ -0,0 +1,218 @@
+serializesValues = $serializesValues;
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param string|array $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ if (! isset($this->storage[$key])) {
+ return;
+ }
+
+ $item = $this->storage[$key];
+
+ $expiresAt = $item['expiresAt'] ?? 0;
+
+ if ($expiresAt !== 0 && $this->currentTime() > $expiresAt) {
+ $this->forget($key);
+
+ return;
+ }
+
+ return $this->serializesValues ? unserialize($item['value']) : $item['value'];
+ }
+
+ /**
+ * Store an item in the cache for a given number of seconds.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ $this->storage[$key] = [
+ 'value' => $this->serializesValues ? serialize($value) : $value,
+ 'expiresAt' => $this->calculateExpiration($seconds),
+ ];
+
+ return true;
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int
+ */
+ public function increment($key, $value = 1)
+ {
+ if (! is_null($existing = $this->get($key))) {
+ return tap(((int) $existing) + $value, function ($incremented) use ($key) {
+ $value = $this->serializesValues ? serialize($incremented) : $incremented;
+
+ $this->storage[$key]['value'] = $value;
+ });
+ }
+
+ $this->forever($key, $value);
+
+ return $value;
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->increment($key, $value * -1);
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ return $this->put($key, $value, 0);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ if (array_key_exists($key, $this->storage)) {
+ unset($this->storage[$key]);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ $this->storage = [];
+
+ return true;
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return '';
+ }
+
+ /**
+ * Get the expiration time of the key.
+ *
+ * @param int $seconds
+ * @return int
+ */
+ protected function calculateExpiration($seconds)
+ {
+ return $this->toTimestamp($seconds);
+ }
+
+ /**
+ * Get the UNIX timestamp for the given number of seconds.
+ *
+ * @param int $seconds
+ * @return int
+ */
+ protected function toTimestamp($seconds)
+ {
+ return $seconds > 0 ? $this->availableAt($seconds) : 0;
+ }
+
+ /**
+ * Get a lock instance.
+ *
+ * @param string $name
+ * @param int $seconds
+ * @param string|null $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function lock($name, $seconds = 0, $owner = null)
+ {
+ return new ArrayLock($this, $name, $seconds, $owner);
+ }
+
+ /**
+ * Restore a lock instance using the owner identifier.
+ *
+ * @param string $name
+ * @param string $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function restoreLock($name, $owner)
+ {
+ return $this->lock($name, 0, $owner);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/CacheLock.php b/vendor/laravel/framework/src/Illuminate/Cache/CacheLock.php
new file mode 100644
index 0000000..5cc4eea
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/CacheLock.php
@@ -0,0 +1,85 @@
+store = $store;
+ }
+
+ /**
+ * Attempt to acquire the lock.
+ *
+ * @return bool
+ */
+ public function acquire()
+ {
+ if (method_exists($this->store, 'add') && $this->seconds > 0) {
+ return $this->store->add(
+ $this->name, $this->owner, $this->seconds
+ );
+ }
+
+ if (! is_null($this->store->get($this->name))) {
+ return false;
+ }
+
+ return ($this->seconds > 0)
+ ? $this->store->put($this->name, $this->owner, $this->seconds)
+ : $this->store->forever($this->name, $this->owner);
+ }
+
+ /**
+ * Release the lock.
+ *
+ * @return bool
+ */
+ public function release()
+ {
+ if ($this->isOwnedByCurrentProcess()) {
+ return $this->store->forget($this->name);
+ }
+
+ return false;
+ }
+
+ /**
+ * Releases this lock regardless of ownership.
+ *
+ * @return void
+ */
+ public function forceRelease()
+ {
+ $this->store->forget($this->name);
+ }
+
+ /**
+ * Returns the owner value written into the driver for this lock.
+ *
+ * @return mixed
+ */
+ protected function getCurrentOwner()
+ {
+ return $this->store->get($this->name);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/CacheManager.php b/vendor/laravel/framework/src/Illuminate/Cache/CacheManager.php
new file mode 100755
index 0000000..9d87148
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/CacheManager.php
@@ -0,0 +1,420 @@
+app = $app;
+ }
+
+ /**
+ * Get a cache store instance by name, wrapped in a repository.
+ *
+ * @param string|null $name
+ * @return \Illuminate\Contracts\Cache\Repository
+ */
+ public function store($name = null)
+ {
+ $name = $name ?: $this->getDefaultDriver();
+
+ return $this->stores[$name] = $this->get($name);
+ }
+
+ /**
+ * Get a cache driver instance.
+ *
+ * @param string|null $driver
+ * @return \Illuminate\Contracts\Cache\Repository
+ */
+ public function driver($driver = null)
+ {
+ return $this->store($driver);
+ }
+
+ /**
+ * Attempt to get the store from the local cache.
+ *
+ * @param string $name
+ * @return \Illuminate\Contracts\Cache\Repository
+ */
+ protected function get($name)
+ {
+ return $this->stores[$name] ?? $this->resolve($name);
+ }
+
+ /**
+ * Resolve the given store.
+ *
+ * @param string $name
+ * @return \Illuminate\Contracts\Cache\Repository
+ *
+ * @throws \InvalidArgumentException
+ */
+ protected function resolve($name)
+ {
+ $config = $this->getConfig($name);
+
+ if (is_null($config)) {
+ throw new InvalidArgumentException("Cache store [{$name}] is not defined.");
+ }
+
+ if (isset($this->customCreators[$config['driver']])) {
+ return $this->callCustomCreator($config);
+ } else {
+ $driverMethod = 'create'.ucfirst($config['driver']).'Driver';
+
+ if (method_exists($this, $driverMethod)) {
+ return $this->{$driverMethod}($config);
+ } else {
+ throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported.");
+ }
+ }
+ }
+
+ /**
+ * Call a custom driver creator.
+ *
+ * @param array $config
+ * @return mixed
+ */
+ protected function callCustomCreator(array $config)
+ {
+ return $this->customCreators[$config['driver']]($this->app, $config);
+ }
+
+ /**
+ * Create an instance of the APC cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createApcDriver(array $config)
+ {
+ $prefix = $this->getPrefix($config);
+
+ return $this->repository(new ApcStore(new ApcWrapper, $prefix));
+ }
+
+ /**
+ * Create an instance of the array cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createArrayDriver(array $config)
+ {
+ return $this->repository(new ArrayStore($config['serialize'] ?? false));
+ }
+
+ /**
+ * Create an instance of the file cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createFileDriver(array $config)
+ {
+ return $this->repository(new FileStore($this->app['files'], $config['path'], $config['permission'] ?? null));
+ }
+
+ /**
+ * Create an instance of the Memcached cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createMemcachedDriver(array $config)
+ {
+ $prefix = $this->getPrefix($config);
+
+ $memcached = $this->app['memcached.connector']->connect(
+ $config['servers'],
+ $config['persistent_id'] ?? null,
+ $config['options'] ?? [],
+ array_filter($config['sasl'] ?? [])
+ );
+
+ return $this->repository(new MemcachedStore($memcached, $prefix));
+ }
+
+ /**
+ * Create an instance of the Null cache driver.
+ *
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createNullDriver()
+ {
+ return $this->repository(new NullStore);
+ }
+
+ /**
+ * Create an instance of the Redis cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createRedisDriver(array $config)
+ {
+ $redis = $this->app['redis'];
+
+ $connection = $config['connection'] ?? 'default';
+
+ $store = new RedisStore($redis, $this->getPrefix($config), $connection);
+
+ return $this->repository(
+ $store->setLockConnection($config['lock_connection'] ?? $connection)
+ );
+ }
+
+ /**
+ * Create an instance of the database cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createDatabaseDriver(array $config)
+ {
+ $connection = $this->app['db']->connection($config['connection'] ?? null);
+
+ $store = new DatabaseStore(
+ $connection,
+ $config['table'],
+ $this->getPrefix($config),
+ $config['lock_table'] ?? 'cache_locks',
+ $config['lock_lottery'] ?? [2, 100]
+ );
+
+ return $this->repository($store->setLockConnection(
+ $this->app['db']->connection($config['lock_connection'] ?? $config['connection'] ?? null)
+ ));
+ }
+
+ /**
+ * Create an instance of the DynamoDB cache driver.
+ *
+ * @param array $config
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function createDynamodbDriver(array $config)
+ {
+ $client = $this->newDynamodbClient($config);
+
+ return $this->repository(
+ new DynamoDbStore(
+ $client,
+ $config['table'],
+ $config['attributes']['key'] ?? 'key',
+ $config['attributes']['value'] ?? 'value',
+ $config['attributes']['expiration'] ?? 'expires_at',
+ $this->getPrefix($config)
+ )
+ );
+ }
+
+ /**
+ * Create new DynamoDb Client instance.
+ *
+ * @return \Aws\DynamoDb\DynamoDbClient
+ */
+ protected function newDynamodbClient(array $config)
+ {
+ $dynamoConfig = [
+ 'region' => $config['region'],
+ 'version' => 'latest',
+ 'endpoint' => $config['endpoint'] ?? null,
+ ];
+
+ if (isset($config['key'], $config['secret'])) {
+ $dynamoConfig['credentials'] = Arr::only(
+ $config, ['key', 'secret', 'token']
+ );
+ }
+
+ return new DynamoDbClient($dynamoConfig);
+ }
+
+ /**
+ * Create a new cache repository with the given implementation.
+ *
+ * @param \Illuminate\Contracts\Cache\Store $store
+ * @return \Illuminate\Cache\Repository
+ */
+ public function repository(Store $store)
+ {
+ return tap(new Repository($store), function ($repository) {
+ $this->setEventDispatcher($repository);
+ });
+ }
+
+ /**
+ * Set the event dispatcher on the given repository instance.
+ *
+ * @param \Illuminate\Cache\Repository $repository
+ * @return void
+ */
+ protected function setEventDispatcher(Repository $repository)
+ {
+ if (! $this->app->bound(DispatcherContract::class)) {
+ return;
+ }
+
+ $repository->setEventDispatcher(
+ $this->app[DispatcherContract::class]
+ );
+ }
+
+ /**
+ * Re-set the event dispatcher on all resolved cache repositories.
+ *
+ * @return void
+ */
+ public function refreshEventDispatcher()
+ {
+ array_map([$this, 'setEventDispatcher'], $this->stores);
+ }
+
+ /**
+ * Get the cache prefix.
+ *
+ * @param array $config
+ * @return string
+ */
+ protected function getPrefix(array $config)
+ {
+ return $config['prefix'] ?? $this->app['config']['cache.prefix'];
+ }
+
+ /**
+ * Get the cache connection configuration.
+ *
+ * @param string $name
+ * @return array|null
+ */
+ protected function getConfig($name)
+ {
+ if (! is_null($name) && $name !== 'null') {
+ return $this->app['config']["cache.stores.{$name}"];
+ }
+
+ return ['driver' => 'null'];
+ }
+
+ /**
+ * Get the default cache driver name.
+ *
+ * @return string
+ */
+ public function getDefaultDriver()
+ {
+ return $this->app['config']['cache.default'];
+ }
+
+ /**
+ * Set the default cache driver name.
+ *
+ * @param string $name
+ * @return void
+ */
+ public function setDefaultDriver($name)
+ {
+ $this->app['config']['cache.default'] = $name;
+ }
+
+ /**
+ * Unset the given driver instances.
+ *
+ * @param array|string|null $name
+ * @return $this
+ */
+ public function forgetDriver($name = null)
+ {
+ $name ??= $this->getDefaultDriver();
+
+ foreach ((array) $name as $cacheName) {
+ if (isset($this->stores[$cacheName])) {
+ unset($this->stores[$cacheName]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Disconnect the given driver and remove from local cache.
+ *
+ * @param string|null $name
+ * @return void
+ */
+ public function purge($name = null)
+ {
+ $name ??= $this->getDefaultDriver();
+
+ unset($this->stores[$name]);
+ }
+
+ /**
+ * Register a custom driver creator Closure.
+ *
+ * @param string $driver
+ * @param \Closure $callback
+ * @return $this
+ */
+ public function extend($driver, Closure $callback)
+ {
+ $this->customCreators[$driver] = $callback->bindTo($this, $this);
+
+ return $this;
+ }
+
+ /**
+ * Dynamically call the default driver instance.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ return $this->store()->$method(...$parameters);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php b/vendor/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php
new file mode 100755
index 0000000..662d556
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/CacheServiceProvider.php
@@ -0,0 +1,52 @@
+app->singleton('cache', function ($app) {
+ return new CacheManager($app);
+ });
+
+ $this->app->singleton('cache.store', function ($app) {
+ return $app['cache']->driver();
+ });
+
+ $this->app->singleton('cache.psr6', function ($app) {
+ return new Psr16Adapter($app['cache.store']);
+ });
+
+ $this->app->singleton('memcached.connector', function () {
+ return new MemcachedConnector;
+ });
+
+ $this->app->singleton(RateLimiter::class, function ($app) {
+ return new RateLimiter($app->make('cache')->driver(
+ $app['config']->get('cache.limiter')
+ ));
+ });
+ }
+
+ /**
+ * Get the services provided by the provider.
+ *
+ * @return array
+ */
+ public function provides()
+ {
+ return [
+ 'cache', 'cache.store', 'cache.psr6', 'memcached.connector', RateLimiter::class,
+ ];
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/Console/CacheTableCommand.php b/vendor/laravel/framework/src/Illuminate/Cache/Console/CacheTableCommand.php
new file mode 100644
index 0000000..ea18d4f
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/Console/CacheTableCommand.php
@@ -0,0 +1,94 @@
+files = $files;
+ $this->composer = $composer;
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ $fullPath = $this->createBaseMigration();
+
+ $this->files->put($fullPath, $this->files->get(__DIR__.'/stubs/cache.stub'));
+
+ $this->info('Migration created successfully.');
+
+ $this->composer->dumpAutoloads();
+ }
+
+ /**
+ * Create a base migration file for the table.
+ *
+ * @return string
+ */
+ protected function createBaseMigration()
+ {
+ $name = 'create_cache_table';
+
+ $path = $this->laravel->databasePath().'/migrations';
+
+ return $this->laravel['migration.creator']->create($name, $path);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/Console/ClearCommand.php b/vendor/laravel/framework/src/Illuminate/Cache/Console/ClearCommand.php
new file mode 100755
index 0000000..ca8d2a2
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/Console/ClearCommand.php
@@ -0,0 +1,158 @@
+cache = $cache;
+ $this->files = $files;
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ $this->laravel['events']->dispatch(
+ 'cache:clearing', [$this->argument('store'), $this->tags()]
+ );
+
+ $successful = $this->cache()->flush();
+
+ $this->flushFacades();
+
+ if (! $successful) {
+ return $this->error('Failed to clear cache. Make sure you have the appropriate permissions.');
+ }
+
+ $this->laravel['events']->dispatch(
+ 'cache:cleared', [$this->argument('store'), $this->tags()]
+ );
+
+ $this->info('Application cache cleared successfully.');
+ }
+
+ /**
+ * Flush the real-time facades stored in the cache directory.
+ *
+ * @return void
+ */
+ public function flushFacades()
+ {
+ if (! $this->files->exists($storagePath = storage_path('framework/cache'))) {
+ return;
+ }
+
+ foreach ($this->files->files($storagePath) as $file) {
+ if (preg_match('/facade-.*\.php$/', $file)) {
+ $this->files->delete($file);
+ }
+ }
+ }
+
+ /**
+ * Get the cache instance for the command.
+ *
+ * @return \Illuminate\Cache\Repository
+ */
+ protected function cache()
+ {
+ $cache = $this->cache->store($this->argument('store'));
+
+ return empty($this->tags()) ? $cache : $cache->tags($this->tags());
+ }
+
+ /**
+ * Get the tags passed to the command.
+ *
+ * @return array
+ */
+ protected function tags()
+ {
+ return array_filter(explode(',', $this->option('tags') ?? ''));
+ }
+
+ /**
+ * Get the console command arguments.
+ *
+ * @return array
+ */
+ protected function getArguments()
+ {
+ return [
+ ['store', InputArgument::OPTIONAL, 'The name of the store you would like to clear'],
+ ];
+ }
+
+ /**
+ * Get the console command options.
+ *
+ * @return array
+ */
+ protected function getOptions()
+ {
+ return [
+ ['tags', null, InputOption::VALUE_OPTIONAL, 'The cache tags you would like to clear', null],
+ ];
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/Console/ForgetCommand.php b/vendor/laravel/framework/src/Illuminate/Cache/Console/ForgetCommand.php
new file mode 100755
index 0000000..41e7adb
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/Console/ForgetCommand.php
@@ -0,0 +1,70 @@
+cache = $cache;
+ }
+
+ /**
+ * Execute the console command.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ $this->cache->store($this->argument('store'))->forget(
+ $this->argument('key')
+ );
+
+ $this->info('The ['.$this->argument('key').'] key has been removed from the cache.');
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/Console/stubs/cache.stub b/vendor/laravel/framework/src/Illuminate/Cache/Console/stubs/cache.stub
new file mode 100644
index 0000000..eee35e9
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/Console/stubs/cache.stub
@@ -0,0 +1,39 @@
+string('key')->primary();
+ $table->mediumText('value');
+ $table->integer('expiration');
+ });
+
+ Schema::create('cache_locks', function (Blueprint $table) {
+ $table->string('key')->primary();
+ $table->string('owner');
+ $table->integer('expiration');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('cache');
+ Schema::dropIfExists('cache_locks');
+ }
+};
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/DatabaseLock.php b/vendor/laravel/framework/src/Illuminate/Cache/DatabaseLock.php
new file mode 100644
index 0000000..c8db07a
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/DatabaseLock.php
@@ -0,0 +1,147 @@
+connection = $connection;
+ $this->table = $table;
+ $this->lottery = $lottery;
+ }
+
+ /**
+ * Attempt to acquire the lock.
+ *
+ * @return bool
+ */
+ public function acquire()
+ {
+ try {
+ $this->connection->table($this->table)->insert([
+ 'key' => $this->name,
+ 'owner' => $this->owner,
+ 'expiration' => $this->expiresAt(),
+ ]);
+
+ $acquired = true;
+ } catch (QueryException $e) {
+ $updated = $this->connection->table($this->table)
+ ->where('key', $this->name)
+ ->where(function ($query) {
+ return $query->where('owner', $this->owner)->orWhere('expiration', '<=', time());
+ })->update([
+ 'owner' => $this->owner,
+ 'expiration' => $this->expiresAt(),
+ ]);
+
+ $acquired = $updated >= 1;
+ }
+
+ if (random_int(1, $this->lottery[1]) <= $this->lottery[0]) {
+ $this->connection->table($this->table)->where('expiration', '<=', time())->delete();
+ }
+
+ return $acquired;
+ }
+
+ /**
+ * Get the UNIX timestamp indicating when the lock should expire.
+ *
+ * @return int
+ */
+ protected function expiresAt()
+ {
+ return $this->seconds > 0 ? time() + $this->seconds : Carbon::now()->addDays(1)->getTimestamp();
+ }
+
+ /**
+ * Release the lock.
+ *
+ * @return bool
+ */
+ public function release()
+ {
+ if ($this->isOwnedByCurrentProcess()) {
+ $this->connection->table($this->table)
+ ->where('key', $this->name)
+ ->where('owner', $this->owner)
+ ->delete();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Releases this lock in disregard of ownership.
+ *
+ * @return void
+ */
+ public function forceRelease()
+ {
+ $this->connection->table($this->table)
+ ->where('key', $this->name)
+ ->delete();
+ }
+
+ /**
+ * Returns the owner value written into the driver for this lock.
+ *
+ * @return string
+ */
+ protected function getCurrentOwner()
+ {
+ return optional($this->connection->table($this->table)->where('key', $this->name)->first())->owner;
+ }
+
+ /**
+ * Get the name of the database connection being used to manage the lock.
+ *
+ * @return string
+ */
+ public function getConnectionName()
+ {
+ return $this->connection->getName();
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/DatabaseStore.php b/vendor/laravel/framework/src/Illuminate/Cache/DatabaseStore.php
new file mode 100755
index 0000000..b43761b
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/DatabaseStore.php
@@ -0,0 +1,395 @@
+table = $table;
+ $this->prefix = $prefix;
+ $this->connection = $connection;
+ $this->lockTable = $lockTable;
+ $this->lockLottery = $lockLottery;
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param string|array $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ $prefixed = $this->prefix.$key;
+
+ $cache = $this->table()->where('key', '=', $prefixed)->first();
+
+ // If we have a cache record we will check the expiration time against current
+ // time on the system and see if the record has expired. If it has, we will
+ // remove the records from the database table so it isn't returned again.
+ if (is_null($cache)) {
+ return;
+ }
+
+ $cache = is_array($cache) ? (object) $cache : $cache;
+
+ // If this cache expiration date is past the current time, we will remove this
+ // item from the cache. Then we will return a null value since the cache is
+ // expired. We will use "Carbon" to make this comparison with the column.
+ if ($this->currentTime() >= $cache->expiration) {
+ $this->forget($key);
+
+ return;
+ }
+
+ return $this->unserialize($cache->value);
+ }
+
+ /**
+ * Store an item in the cache for a given number of seconds.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ $key = $this->prefix.$key;
+ $value = $this->serialize($value);
+ $expiration = $this->getTime() + $seconds;
+
+ try {
+ return $this->table()->insert(compact('key', 'value', 'expiration'));
+ } catch (Exception $e) {
+ $result = $this->table()->where('key', $key)->update(compact('value', 'expiration'));
+
+ return $result > 0;
+ }
+ }
+
+ /**
+ * Store an item in the cache if the key doesn't exist.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function add($key, $value, $seconds)
+ {
+ $key = $this->prefix.$key;
+ $value = $this->serialize($value);
+ $expiration = $this->getTime() + $seconds;
+
+ try {
+ return $this->table()->insert(compact('key', 'value', 'expiration'));
+ } catch (QueryException $e) {
+ return $this->table()
+ ->where('key', $key)
+ ->where('expiration', '<=', $this->getTime())
+ ->update([
+ 'value' => $value,
+ 'expiration' => $expiration,
+ ]) >= 1;
+ }
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function increment($key, $value = 1)
+ {
+ return $this->incrementOrDecrement($key, $value, function ($current, $value) {
+ return $current + $value;
+ });
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->incrementOrDecrement($key, $value, function ($current, $value) {
+ return $current - $value;
+ });
+ }
+
+ /**
+ * Increment or decrement an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param \Closure $callback
+ * @return int|bool
+ */
+ protected function incrementOrDecrement($key, $value, Closure $callback)
+ {
+ return $this->connection->transaction(function () use ($key, $value, $callback) {
+ $prefixed = $this->prefix.$key;
+
+ $cache = $this->table()->where('key', $prefixed)
+ ->lockForUpdate()->first();
+
+ // If there is no value in the cache, we will return false here. Otherwise the
+ // value will be decrypted and we will proceed with this function to either
+ // increment or decrement this value based on the given action callbacks.
+ if (is_null($cache)) {
+ return false;
+ }
+
+ $cache = is_array($cache) ? (object) $cache : $cache;
+
+ $current = $this->unserialize($cache->value);
+
+ // Here we'll call this callback function that was given to the function which
+ // is used to either increment or decrement the function. We use a callback
+ // so we do not have to recreate all this logic in each of the functions.
+ $new = $callback((int) $current, $value);
+
+ if (! is_numeric($current)) {
+ return false;
+ }
+
+ // Here we will update the values in the table. We will also encrypt the value
+ // since database cache values are encrypted by default with secure storage
+ // that can't be easily read. We will return the new value after storing.
+ $this->table()->where('key', $prefixed)->update([
+ 'value' => $this->serialize($new),
+ ]);
+
+ return $new;
+ });
+ }
+
+ /**
+ * Get the current system time.
+ *
+ * @return int
+ */
+ protected function getTime()
+ {
+ return $this->currentTime();
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ return $this->put($key, $value, 315360000);
+ }
+
+ /**
+ * Get a lock instance.
+ *
+ * @param string $name
+ * @param int $seconds
+ * @param string|null $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function lock($name, $seconds = 0, $owner = null)
+ {
+ return new DatabaseLock(
+ $this->lockConnection ?? $this->connection,
+ $this->lockTable,
+ $this->prefix.$name,
+ $seconds,
+ $owner,
+ $this->lockLottery
+ );
+ }
+
+ /**
+ * Restore a lock instance using the owner identifier.
+ *
+ * @param string $name
+ * @param string $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function restoreLock($name, $owner)
+ {
+ return $this->lock($name, 0, $owner);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ $this->table()->where('key', '=', $this->prefix.$key)->delete();
+
+ return true;
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ $this->table()->delete();
+
+ return true;
+ }
+
+ /**
+ * Get a query builder for the cache table.
+ *
+ * @return \Illuminate\Database\Query\Builder
+ */
+ protected function table()
+ {
+ return $this->connection->table($this->table);
+ }
+
+ /**
+ * Get the underlying database connection.
+ *
+ * @return \Illuminate\Database\ConnectionInterface
+ */
+ public function getConnection()
+ {
+ return $this->connection;
+ }
+
+ /**
+ * Specify the name of the connection that should be used to manage locks.
+ *
+ * @param \Illuminate\Database\ConnectionInterface $connection
+ * @return $this
+ */
+ public function setLockConnection($connection)
+ {
+ $this->lockConnection = $connection;
+
+ return $this;
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return $this->prefix;
+ }
+
+ /**
+ * Serialize the given value.
+ *
+ * @param mixed $value
+ * @return string
+ */
+ protected function serialize($value)
+ {
+ $result = serialize($value);
+
+ if ($this->connection instanceof PostgresConnection && str_contains($result, "\0")) {
+ $result = base64_encode($result);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Unserialize the given value.
+ *
+ * @param string $value
+ * @return mixed
+ */
+ protected function unserialize($value)
+ {
+ if ($this->connection instanceof PostgresConnection && ! Str::contains($value, [':', ';'])) {
+ $value = base64_decode($value);
+ }
+
+ return unserialize($value);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/DynamoDbLock.php b/vendor/laravel/framework/src/Illuminate/Cache/DynamoDbLock.php
new file mode 100644
index 0000000..54eec53
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/DynamoDbLock.php
@@ -0,0 +1,75 @@
+dynamo = $dynamo;
+ }
+
+ /**
+ * Attempt to acquire the lock.
+ *
+ * @return bool
+ */
+ public function acquire()
+ {
+ return $this->dynamo->add(
+ $this->name, $this->owner, $this->seconds
+ );
+ }
+
+ /**
+ * Release the lock.
+ *
+ * @return bool
+ */
+ public function release()
+ {
+ if ($this->isOwnedByCurrentProcess()) {
+ return $this->dynamo->forget($this->name);
+ }
+
+ return false;
+ }
+
+ /**
+ * Release this lock in disregard of ownership.
+ *
+ * @return void
+ */
+ public function forceRelease()
+ {
+ $this->dynamo->forget($this->name);
+ }
+
+ /**
+ * Returns the owner value written into the driver for this lock.
+ *
+ * @return mixed
+ */
+ protected function getCurrentOwner()
+ {
+ return $this->dynamo->get($this->name);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/DynamoDbStore.php b/vendor/laravel/framework/src/Illuminate/Cache/DynamoDbStore.php
new file mode 100644
index 0000000..30e175a
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/DynamoDbStore.php
@@ -0,0 +1,538 @@
+table = $table;
+ $this->dynamo = $dynamo;
+ $this->keyAttribute = $keyAttribute;
+ $this->valueAttribute = $valueAttribute;
+ $this->expirationAttribute = $expirationAttribute;
+
+ $this->setPrefix($prefix);
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ $response = $this->dynamo->getItem([
+ 'TableName' => $this->table,
+ 'ConsistentRead' => false,
+ 'Key' => [
+ $this->keyAttribute => [
+ 'S' => $this->prefix.$key,
+ ],
+ ],
+ ]);
+
+ if (! isset($response['Item'])) {
+ return;
+ }
+
+ if ($this->isExpired($response['Item'])) {
+ return;
+ }
+
+ if (isset($response['Item'][$this->valueAttribute])) {
+ return $this->unserialize(
+ $response['Item'][$this->valueAttribute]['S'] ??
+ $response['Item'][$this->valueAttribute]['N'] ??
+ null
+ );
+ }
+ }
+
+ /**
+ * Retrieve multiple items from the cache by key.
+ *
+ * Items not found in the cache will have a null value.
+ *
+ * @param array $keys
+ * @return array
+ */
+ public function many(array $keys)
+ {
+ $prefixedKeys = array_map(function ($key) {
+ return $this->prefix.$key;
+ }, $keys);
+
+ $response = $this->dynamo->batchGetItem([
+ 'RequestItems' => [
+ $this->table => [
+ 'ConsistentRead' => false,
+ 'Keys' => collect($prefixedKeys)->map(function ($key) {
+ return [
+ $this->keyAttribute => [
+ 'S' => $key,
+ ],
+ ];
+ })->all(),
+ ],
+ ],
+ ]);
+
+ $now = Carbon::now();
+
+ return array_merge(collect(array_flip($keys))->map(function () {
+ //
+ })->all(), collect($response['Responses'][$this->table])->mapWithKeys(function ($response) use ($now) {
+ if ($this->isExpired($response, $now)) {
+ $value = null;
+ } else {
+ $value = $this->unserialize(
+ $response[$this->valueAttribute]['S'] ??
+ $response[$this->valueAttribute]['N'] ??
+ null
+ );
+ }
+
+ return [Str::replaceFirst($this->prefix, '', $response[$this->keyAttribute]['S']) => $value];
+ })->all());
+ }
+
+ /**
+ * Determine if the given item is expired.
+ *
+ * @param array $item
+ * @param \DateTimeInterface|null $expiration
+ * @return bool
+ */
+ protected function isExpired(array $item, $expiration = null)
+ {
+ $expiration = $expiration ?: Carbon::now();
+
+ return isset($item[$this->expirationAttribute]) &&
+ $expiration->getTimestamp() >= $item[$this->expirationAttribute]['N'];
+ }
+
+ /**
+ * Store an item in the cache for a given number of seconds.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ $this->dynamo->putItem([
+ 'TableName' => $this->table,
+ 'Item' => [
+ $this->keyAttribute => [
+ 'S' => $this->prefix.$key,
+ ],
+ $this->valueAttribute => [
+ $this->type($value) => $this->serialize($value),
+ ],
+ $this->expirationAttribute => [
+ 'N' => (string) $this->toTimestamp($seconds),
+ ],
+ ],
+ ]);
+
+ return true;
+ }
+
+ /**
+ * Store multiple items in the cache for a given number of seconds.
+ *
+ * @param array $values
+ * @param int $seconds
+ * @return bool
+ */
+ public function putMany(array $values, $seconds)
+ {
+ $expiration = $this->toTimestamp($seconds);
+
+ $this->dynamo->batchWriteItem([
+ 'RequestItems' => [
+ $this->table => collect($values)->map(function ($value, $key) use ($expiration) {
+ return [
+ 'PutRequest' => [
+ 'Item' => [
+ $this->keyAttribute => [
+ 'S' => $this->prefix.$key,
+ ],
+ $this->valueAttribute => [
+ $this->type($value) => $this->serialize($value),
+ ],
+ $this->expirationAttribute => [
+ 'N' => (string) $expiration,
+ ],
+ ],
+ ],
+ ];
+ })->values()->all(),
+ ],
+ ]);
+
+ return true;
+ }
+
+ /**
+ * Store an item in the cache if the key doesn't exist.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function add($key, $value, $seconds)
+ {
+ try {
+ $this->dynamo->putItem([
+ 'TableName' => $this->table,
+ 'Item' => [
+ $this->keyAttribute => [
+ 'S' => $this->prefix.$key,
+ ],
+ $this->valueAttribute => [
+ $this->type($value) => $this->serialize($value),
+ ],
+ $this->expirationAttribute => [
+ 'N' => (string) $this->toTimestamp($seconds),
+ ],
+ ],
+ 'ConditionExpression' => 'attribute_not_exists(#key) OR #expires_at < :now',
+ 'ExpressionAttributeNames' => [
+ '#key' => $this->keyAttribute,
+ '#expires_at' => $this->expirationAttribute,
+ ],
+ 'ExpressionAttributeValues' => [
+ ':now' => [
+ 'N' => (string) Carbon::now()->getTimestamp(),
+ ],
+ ],
+ ]);
+
+ return true;
+ } catch (DynamoDbException $e) {
+ if (str_contains($e->getMessage(), 'ConditionalCheckFailed')) {
+ return false;
+ }
+
+ throw $e;
+ }
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function increment($key, $value = 1)
+ {
+ try {
+ $response = $this->dynamo->updateItem([
+ 'TableName' => $this->table,
+ 'Key' => [
+ $this->keyAttribute => [
+ 'S' => $this->prefix.$key,
+ ],
+ ],
+ 'ConditionExpression' => 'attribute_exists(#key) AND #expires_at > :now',
+ 'UpdateExpression' => 'SET #value = #value + :amount',
+ 'ExpressionAttributeNames' => [
+ '#key' => $this->keyAttribute,
+ '#value' => $this->valueAttribute,
+ '#expires_at' => $this->expirationAttribute,
+ ],
+ 'ExpressionAttributeValues' => [
+ ':now' => [
+ 'N' => (string) Carbon::now()->getTimestamp(),
+ ],
+ ':amount' => [
+ 'N' => (string) $value,
+ ],
+ ],
+ 'ReturnValues' => 'UPDATED_NEW',
+ ]);
+
+ return (int) $response['Attributes'][$this->valueAttribute]['N'];
+ } catch (DynamoDbException $e) {
+ if (str_contains($e->getMessage(), 'ConditionalCheckFailed')) {
+ return false;
+ }
+
+ throw $e;
+ }
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function decrement($key, $value = 1)
+ {
+ try {
+ $response = $this->dynamo->updateItem([
+ 'TableName' => $this->table,
+ 'Key' => [
+ $this->keyAttribute => [
+ 'S' => $this->prefix.$key,
+ ],
+ ],
+ 'ConditionExpression' => 'attribute_exists(#key) AND #expires_at > :now',
+ 'UpdateExpression' => 'SET #value = #value - :amount',
+ 'ExpressionAttributeNames' => [
+ '#key' => $this->keyAttribute,
+ '#value' => $this->valueAttribute,
+ '#expires_at' => $this->expirationAttribute,
+ ],
+ 'ExpressionAttributeValues' => [
+ ':now' => [
+ 'N' => (string) Carbon::now()->getTimestamp(),
+ ],
+ ':amount' => [
+ 'N' => (string) $value,
+ ],
+ ],
+ 'ReturnValues' => 'UPDATED_NEW',
+ ]);
+
+ return (int) $response['Attributes'][$this->valueAttribute]['N'];
+ } catch (DynamoDbException $e) {
+ if (str_contains($e->getMessage(), 'ConditionalCheckFailed')) {
+ return false;
+ }
+
+ throw $e;
+ }
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ return $this->put($key, $value, Carbon::now()->addYears(5)->getTimestamp());
+ }
+
+ /**
+ * Get a lock instance.
+ *
+ * @param string $name
+ * @param int $seconds
+ * @param string|null $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function lock($name, $seconds = 0, $owner = null)
+ {
+ return new DynamoDbLock($this, $this->prefix.$name, $seconds, $owner);
+ }
+
+ /**
+ * Restore a lock instance using the owner identifier.
+ *
+ * @param string $name
+ * @param string $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function restoreLock($name, $owner)
+ {
+ return $this->lock($name, 0, $owner);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ $this->dynamo->deleteItem([
+ 'TableName' => $this->table,
+ 'Key' => [
+ $this->keyAttribute => [
+ 'S' => $this->prefix.$key,
+ ],
+ ],
+ ]);
+
+ return true;
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ *
+ * @throws \RuntimeException
+ */
+ public function flush()
+ {
+ throw new RuntimeException('DynamoDb does not support flushing an entire table. Please create a new table.');
+ }
+
+ /**
+ * Get the UNIX timestamp for the given number of seconds.
+ *
+ * @param int $seconds
+ * @return int
+ */
+ protected function toTimestamp($seconds)
+ {
+ return $seconds > 0
+ ? $this->availableAt($seconds)
+ : Carbon::now()->getTimestamp();
+ }
+
+ /**
+ * Serialize the value.
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function serialize($value)
+ {
+ return is_numeric($value) ? (string) $value : serialize($value);
+ }
+
+ /**
+ * Unserialize the value.
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function unserialize($value)
+ {
+ if (filter_var($value, FILTER_VALIDATE_INT) !== false) {
+ return (int) $value;
+ }
+
+ if (is_numeric($value)) {
+ return (float) $value;
+ }
+
+ return unserialize($value);
+ }
+
+ /**
+ * Get the DynamoDB type for the given value.
+ *
+ * @param mixed $value
+ * @return string
+ */
+ protected function type($value)
+ {
+ return is_numeric($value) ? 'N' : 'S';
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return $this->prefix;
+ }
+
+ /**
+ * Set the cache key prefix.
+ *
+ * @param string $prefix
+ * @return void
+ */
+ public function setPrefix($prefix)
+ {
+ $this->prefix = ! empty($prefix) ? $prefix.':' : '';
+ }
+
+ /**
+ * Get the DynamoDb Client instance.
+ *
+ * @return \Aws\DynamoDb\DynamoDbClient
+ */
+ public function getClient()
+ {
+ return $this->dynamo;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheEvent.php b/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheEvent.php
new file mode 100644
index 0000000..6c9d42c
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheEvent.php
@@ -0,0 +1,46 @@
+key = $key;
+ $this->tags = $tags;
+ }
+
+ /**
+ * Set the tags for the cache event.
+ *
+ * @param array $tags
+ * @return $this
+ */
+ public function setTags($tags)
+ {
+ $this->tags = $tags;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheHit.php b/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheHit.php
new file mode 100644
index 0000000..976c9e4
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheHit.php
@@ -0,0 +1,28 @@
+value = $value;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheMissed.php b/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheMissed.php
new file mode 100644
index 0000000..d2a5c9f
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/Events/CacheMissed.php
@@ -0,0 +1,8 @@
+value = $value;
+ $this->seconds = $seconds;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/FileStore.php b/vendor/laravel/framework/src/Illuminate/Cache/FileStore.php
new file mode 100755
index 0000000..4229229
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/FileStore.php
@@ -0,0 +1,351 @@
+files = $files;
+ $this->directory = $directory;
+ $this->filePermission = $filePermission;
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param string|array $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ return $this->getPayload($key)['data'] ?? null;
+ }
+
+ /**
+ * Store an item in the cache for a given number of seconds.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ $this->ensureCacheDirectoryExists($path = $this->path($key));
+
+ $result = $this->files->put(
+ $path, $this->expiration($seconds).serialize($value), true
+ );
+
+ if ($result !== false && $result > 0) {
+ $this->ensurePermissionsAreCorrect($path);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Store an item in the cache if the key doesn't exist.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function add($key, $value, $seconds)
+ {
+ $this->ensureCacheDirectoryExists($path = $this->path($key));
+
+ $file = new LockableFile($path, 'c+');
+
+ try {
+ $file->getExclusiveLock();
+ } catch (LockTimeoutException $e) {
+ $file->close();
+
+ return false;
+ }
+
+ $expire = $file->read(10);
+
+ if (empty($expire) || $this->currentTime() >= $expire) {
+ $file->truncate()
+ ->write($this->expiration($seconds).serialize($value))
+ ->close();
+
+ $this->ensurePermissionsAreCorrect($path);
+
+ return true;
+ }
+
+ $file->close();
+
+ return false;
+ }
+
+ /**
+ * Create the file cache directory if necessary.
+ *
+ * @param string $path
+ * @return void
+ */
+ protected function ensureCacheDirectoryExists($path)
+ {
+ $directory = dirname($path);
+
+ if (! $this->files->exists($directory)) {
+ $this->files->makeDirectory($directory, 0777, true, true);
+
+ // We're creating two levels of directories (e.g. 7e/24), so we check them both...
+ $this->ensurePermissionsAreCorrect($directory);
+ $this->ensurePermissionsAreCorrect(dirname($directory));
+ }
+ }
+
+ /**
+ * Ensure the created node has the correct permissions.
+ *
+ * @param string $path
+ * @return void
+ */
+ protected function ensurePermissionsAreCorrect($path)
+ {
+ if (is_null($this->filePermission) ||
+ intval($this->files->chmod($path), 8) == $this->filePermission) {
+ return;
+ }
+
+ $this->files->chmod($path, $this->filePermission);
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int
+ */
+ public function increment($key, $value = 1)
+ {
+ $raw = $this->getPayload($key);
+
+ return tap(((int) $raw['data']) + $value, function ($newValue) use ($key, $raw) {
+ $this->put($key, $newValue, $raw['time'] ?? 0);
+ });
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->increment($key, $value * -1);
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ return $this->put($key, $value, 0);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ if ($this->files->exists($file = $this->path($key))) {
+ return $this->files->delete($file);
+ }
+
+ return false;
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ if (! $this->files->isDirectory($this->directory)) {
+ return false;
+ }
+
+ foreach ($this->files->directories($this->directory) as $directory) {
+ $deleted = $this->files->deleteDirectory($directory);
+
+ if (! $deleted || $this->files->exists($directory)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Retrieve an item and expiry time from the cache by key.
+ *
+ * @param string $key
+ * @return array
+ */
+ protected function getPayload($key)
+ {
+ $path = $this->path($key);
+
+ // If the file doesn't exist, we obviously cannot return the cache so we will
+ // just return null. Otherwise, we'll get the contents of the file and get
+ // the expiration UNIX timestamps from the start of the file's contents.
+ try {
+ $expire = substr(
+ $contents = $this->files->get($path, true), 0, 10
+ );
+ } catch (Exception $e) {
+ return $this->emptyPayload();
+ }
+
+ // If the current time is greater than expiration timestamps we will delete
+ // the file and return null. This helps clean up the old files and keeps
+ // this directory much cleaner for us as old files aren't hanging out.
+ if ($this->currentTime() >= $expire) {
+ $this->forget($key);
+
+ return $this->emptyPayload();
+ }
+
+ try {
+ $data = unserialize(substr($contents, 10));
+ } catch (Exception $e) {
+ $this->forget($key);
+
+ return $this->emptyPayload();
+ }
+
+ // Next, we'll extract the number of seconds that are remaining for a cache
+ // so that we can properly retain the time for things like the increment
+ // operation that may be performed on this cache on a later operation.
+ $time = $expire - $this->currentTime();
+
+ return compact('data', 'time');
+ }
+
+ /**
+ * Get a default empty payload for the cache.
+ *
+ * @return array
+ */
+ protected function emptyPayload()
+ {
+ return ['data' => null, 'time' => null];
+ }
+
+ /**
+ * Get the full path for the given cache key.
+ *
+ * @param string $key
+ * @return string
+ */
+ protected function path($key)
+ {
+ $parts = array_slice(str_split($hash = sha1($key), 2), 0, 2);
+
+ return $this->directory.'/'.implode('/', $parts).'/'.$hash;
+ }
+
+ /**
+ * Get the expiration time based on the given seconds.
+ *
+ * @param int $seconds
+ * @return int
+ */
+ protected function expiration($seconds)
+ {
+ $time = $this->availableAt($seconds);
+
+ return $seconds === 0 || $time > 9999999999 ? 9999999999 : $time;
+ }
+
+ /**
+ * Get the Filesystem instance.
+ *
+ * @return \Illuminate\Filesystem\Filesystem
+ */
+ public function getFilesystem()
+ {
+ return $this->files;
+ }
+
+ /**
+ * Get the working directory of the cache.
+ *
+ * @return string
+ */
+ public function getDirectory()
+ {
+ return $this->directory;
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return '';
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/HasCacheLock.php b/vendor/laravel/framework/src/Illuminate/Cache/HasCacheLock.php
new file mode 100644
index 0000000..82ad9c2
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/HasCacheLock.php
@@ -0,0 +1,31 @@
+lock($name, 0, $owner);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/LICENSE.md b/vendor/laravel/framework/src/Illuminate/Cache/LICENSE.md
new file mode 100644
index 0000000..79810c8
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Taylor Otwell
+
+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.
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/Lock.php b/vendor/laravel/framework/src/Illuminate/Cache/Lock.php
new file mode 100644
index 0000000..bed1705
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/Lock.php
@@ -0,0 +1,167 @@
+name = $name;
+ $this->owner = $owner;
+ $this->seconds = $seconds;
+ }
+
+ /**
+ * Attempt to acquire the lock.
+ *
+ * @return bool
+ */
+ abstract public function acquire();
+
+ /**
+ * Release the lock.
+ *
+ * @return bool
+ */
+ abstract public function release();
+
+ /**
+ * Returns the owner value written into the driver for this lock.
+ *
+ * @return string
+ */
+ abstract protected function getCurrentOwner();
+
+ /**
+ * Attempt to acquire the lock.
+ *
+ * @param callable|null $callback
+ * @return mixed
+ */
+ public function get($callback = null)
+ {
+ $result = $this->acquire();
+
+ if ($result && is_callable($callback)) {
+ try {
+ return $callback();
+ } finally {
+ $this->release();
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Attempt to acquire the lock for the given number of seconds.
+ *
+ * @param int $seconds
+ * @param callable|null $callback
+ * @return mixed
+ *
+ * @throws \Illuminate\Contracts\Cache\LockTimeoutException
+ */
+ public function block($seconds, $callback = null)
+ {
+ $starting = $this->currentTime();
+
+ while (! $this->acquire()) {
+ usleep($this->sleepMilliseconds * 1000);
+
+ if ($this->currentTime() - $seconds >= $starting) {
+ throw new LockTimeoutException;
+ }
+ }
+
+ if (is_callable($callback)) {
+ try {
+ return $callback();
+ } finally {
+ $this->release();
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the current owner of the lock.
+ *
+ * @return string
+ */
+ public function owner()
+ {
+ return $this->owner;
+ }
+
+ /**
+ * Determines whether this lock is allowed to release the lock in the driver.
+ *
+ * @return bool
+ */
+ protected function isOwnedByCurrentProcess()
+ {
+ return $this->getCurrentOwner() === $this->owner;
+ }
+
+ /**
+ * Specify the number of milliseconds to sleep in between blocked lock acquisition attempts.
+ *
+ * @param int $milliseconds
+ * @return $this
+ */
+ public function betweenBlockedAttemptsSleepFor($milliseconds)
+ {
+ $this->sleepMilliseconds = $milliseconds;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/LuaScripts.php b/vendor/laravel/framework/src/Illuminate/Cache/LuaScripts.php
new file mode 100644
index 0000000..6d22fcd
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/LuaScripts.php
@@ -0,0 +1,25 @@
+getMemcached(
+ $connectionId, $credentials, $options
+ );
+
+ if (! $memcached->getServerList()) {
+ // For each server in the array, we'll just extract the configuration and add
+ // the server to the Memcached connection. Once we have added all of these
+ // servers we'll verify the connection is successful and return it back.
+ foreach ($servers as $server) {
+ $memcached->addServer(
+ $server['host'], $server['port'], $server['weight']
+ );
+ }
+ }
+
+ return $memcached;
+ }
+
+ /**
+ * Get a new Memcached instance.
+ *
+ * @param string|null $connectionId
+ * @param array $credentials
+ * @param array $options
+ * @return \Memcached
+ */
+ protected function getMemcached($connectionId, array $credentials, array $options)
+ {
+ $memcached = $this->createMemcachedInstance($connectionId);
+
+ if (count($credentials) === 2) {
+ $this->setCredentials($memcached, $credentials);
+ }
+
+ if (count($options)) {
+ $memcached->setOptions($options);
+ }
+
+ return $memcached;
+ }
+
+ /**
+ * Create the Memcached instance.
+ *
+ * @param string|null $connectionId
+ * @return \Memcached
+ */
+ protected function createMemcachedInstance($connectionId)
+ {
+ return empty($connectionId) ? new Memcached : new Memcached($connectionId);
+ }
+
+ /**
+ * Set the SASL credentials on the Memcached connection.
+ *
+ * @param \Memcached $memcached
+ * @param array $credentials
+ * @return void
+ */
+ protected function setCredentials($memcached, $credentials)
+ {
+ [$username, $password] = $credentials;
+
+ $memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
+
+ $memcached->setSaslAuthData($username, $password);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/MemcachedLock.php b/vendor/laravel/framework/src/Illuminate/Cache/MemcachedLock.php
new file mode 100644
index 0000000..0078a09
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/MemcachedLock.php
@@ -0,0 +1,75 @@
+memcached = $memcached;
+ }
+
+ /**
+ * Attempt to acquire the lock.
+ *
+ * @return bool
+ */
+ public function acquire()
+ {
+ return $this->memcached->add(
+ $this->name, $this->owner, $this->seconds
+ );
+ }
+
+ /**
+ * Release the lock.
+ *
+ * @return bool
+ */
+ public function release()
+ {
+ if ($this->isOwnedByCurrentProcess()) {
+ return $this->memcached->delete($this->name);
+ }
+
+ return false;
+ }
+
+ /**
+ * Releases this lock in disregard of ownership.
+ *
+ * @return void
+ */
+ public function forceRelease()
+ {
+ $this->memcached->delete($this->name);
+ }
+
+ /**
+ * Returns the owner value written into the driver for this lock.
+ *
+ * @return mixed
+ */
+ protected function getCurrentOwner()
+ {
+ return $this->memcached->get($this->name);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/MemcachedStore.php b/vendor/laravel/framework/src/Illuminate/Cache/MemcachedStore.php
new file mode 100755
index 0000000..299dab9
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/MemcachedStore.php
@@ -0,0 +1,279 @@
+= 3.0.0.
+ *
+ * @var bool
+ */
+ protected $onVersionThree;
+
+ /**
+ * Create a new Memcached store.
+ *
+ * @param \Memcached $memcached
+ * @param string $prefix
+ * @return void
+ */
+ public function __construct($memcached, $prefix = '')
+ {
+ $this->setPrefix($prefix);
+ $this->memcached = $memcached;
+
+ $this->onVersionThree = (new ReflectionMethod('Memcached', 'getMulti'))
+ ->getNumberOfParameters() == 2;
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ $value = $this->memcached->get($this->prefix.$key);
+
+ if ($this->memcached->getResultCode() == 0) {
+ return $value;
+ }
+ }
+
+ /**
+ * Retrieve multiple items from the cache by key.
+ *
+ * Items not found in the cache will have a null value.
+ *
+ * @param array $keys
+ * @return array
+ */
+ public function many(array $keys)
+ {
+ $prefixedKeys = array_map(function ($key) {
+ return $this->prefix.$key;
+ }, $keys);
+
+ if ($this->onVersionThree) {
+ $values = $this->memcached->getMulti($prefixedKeys, Memcached::GET_PRESERVE_ORDER);
+ } else {
+ $null = null;
+
+ $values = $this->memcached->getMulti($prefixedKeys, $null, Memcached::GET_PRESERVE_ORDER);
+ }
+
+ if ($this->memcached->getResultCode() != 0) {
+ return array_fill_keys($keys, null);
+ }
+
+ return array_combine($keys, $values);
+ }
+
+ /**
+ * Store an item in the cache for a given number of seconds.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ return $this->memcached->set(
+ $this->prefix.$key, $value, $this->calculateExpiration($seconds)
+ );
+ }
+
+ /**
+ * Store multiple items in the cache for a given number of seconds.
+ *
+ * @param array $values
+ * @param int $seconds
+ * @return bool
+ */
+ public function putMany(array $values, $seconds)
+ {
+ $prefixedValues = [];
+
+ foreach ($values as $key => $value) {
+ $prefixedValues[$this->prefix.$key] = $value;
+ }
+
+ return $this->memcached->setMulti(
+ $prefixedValues, $this->calculateExpiration($seconds)
+ );
+ }
+
+ /**
+ * Store an item in the cache if the key doesn't exist.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function add($key, $value, $seconds)
+ {
+ return $this->memcached->add(
+ $this->prefix.$key, $value, $this->calculateExpiration($seconds)
+ );
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function increment($key, $value = 1)
+ {
+ return $this->memcached->increment($this->prefix.$key, $value);
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->memcached->decrement($this->prefix.$key, $value);
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ return $this->put($key, $value, 0);
+ }
+
+ /**
+ * Get a lock instance.
+ *
+ * @param string $name
+ * @param int $seconds
+ * @param string|null $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function lock($name, $seconds = 0, $owner = null)
+ {
+ return new MemcachedLock($this->memcached, $this->prefix.$name, $seconds, $owner);
+ }
+
+ /**
+ * Restore a lock instance using the owner identifier.
+ *
+ * @param string $name
+ * @param string $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function restoreLock($name, $owner)
+ {
+ return $this->lock($name, 0, $owner);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ return $this->memcached->delete($this->prefix.$key);
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ return $this->memcached->flush();
+ }
+
+ /**
+ * Get the expiration time of the key.
+ *
+ * @param int $seconds
+ * @return int
+ */
+ protected function calculateExpiration($seconds)
+ {
+ return $this->toTimestamp($seconds);
+ }
+
+ /**
+ * Get the UNIX timestamp for the given number of seconds.
+ *
+ * @param int $seconds
+ * @return int
+ */
+ protected function toTimestamp($seconds)
+ {
+ return $seconds > 0 ? $this->availableAt($seconds) : 0;
+ }
+
+ /**
+ * Get the underlying Memcached connection.
+ *
+ * @return \Memcached
+ */
+ public function getMemcached()
+ {
+ return $this->memcached;
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return $this->prefix;
+ }
+
+ /**
+ * Set the cache key prefix.
+ *
+ * @param string $prefix
+ * @return void
+ */
+ public function setPrefix($prefix)
+ {
+ $this->prefix = ! empty($prefix) ? $prefix.':' : '';
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/NoLock.php b/vendor/laravel/framework/src/Illuminate/Cache/NoLock.php
new file mode 100644
index 0000000..68560f8
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/NoLock.php
@@ -0,0 +1,46 @@
+owner;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/NullStore.php b/vendor/laravel/framework/src/Illuminate/Cache/NullStore.php
new file mode 100755
index 0000000..5694e6c
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/NullStore.php
@@ -0,0 +1,126 @@
+lock($name, 0, $owner);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ return true;
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ return true;
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return '';
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/PhpRedisLock.php b/vendor/laravel/framework/src/Illuminate/Cache/PhpRedisLock.php
new file mode 100644
index 0000000..6cfce79
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/PhpRedisLock.php
@@ -0,0 +1,35 @@
+redis->eval(
+ LuaScripts::releaseLock(),
+ 1,
+ $this->name,
+ ...$this->redis->pack([$this->owner])
+ );
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/RateLimiter.php b/vendor/laravel/framework/src/Illuminate/Cache/RateLimiter.php
new file mode 100644
index 0000000..32e4b34
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/RateLimiter.php
@@ -0,0 +1,221 @@
+cache = $cache;
+ }
+
+ /**
+ * Register a named limiter configuration.
+ *
+ * @param string $name
+ * @param \Closure $callback
+ * @return $this
+ */
+ public function for(string $name, Closure $callback)
+ {
+ $this->limiters[$name] = $callback;
+
+ return $this;
+ }
+
+ /**
+ * Get the given named rate limiter.
+ *
+ * @param string $name
+ * @return \Closure
+ */
+ public function limiter(string $name)
+ {
+ return $this->limiters[$name] ?? null;
+ }
+
+ /**
+ * Attempts to execute a callback if it's not limited.
+ *
+ * @param string $key
+ * @param int $maxAttempts
+ * @param \Closure $callback
+ * @param int $decaySeconds
+ * @return mixed
+ */
+ public function attempt($key, $maxAttempts, Closure $callback, $decaySeconds = 60)
+ {
+ if ($this->tooManyAttempts($key, $maxAttempts)) {
+ return false;
+ }
+
+ return tap($callback() ?: true, function () use ($key, $decaySeconds) {
+ $this->hit($key, $decaySeconds);
+ });
+ }
+
+ /**
+ * Determine if the given key has been "accessed" too many times.
+ *
+ * @param string $key
+ * @param int $maxAttempts
+ * @return bool
+ */
+ public function tooManyAttempts($key, $maxAttempts)
+ {
+ if ($this->attempts($key) >= $maxAttempts) {
+ if ($this->cache->has($this->cleanRateLimiterKey($key).':timer')) {
+ return true;
+ }
+
+ $this->resetAttempts($key);
+ }
+
+ return false;
+ }
+
+ /**
+ * Increment the counter for a given key for a given decay time.
+ *
+ * @param string $key
+ * @param int $decaySeconds
+ * @return int
+ */
+ public function hit($key, $decaySeconds = 60)
+ {
+ $key = $this->cleanRateLimiterKey($key);
+
+ $this->cache->add(
+ $key.':timer', $this->availableAt($decaySeconds), $decaySeconds
+ );
+
+ $added = $this->cache->add($key, 0, $decaySeconds);
+
+ $hits = (int) $this->cache->increment($key);
+
+ if (! $added && $hits == 1) {
+ $this->cache->put($key, 1, $decaySeconds);
+ }
+
+ return $hits;
+ }
+
+ /**
+ * Get the number of attempts for the given key.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function attempts($key)
+ {
+ $key = $this->cleanRateLimiterKey($key);
+
+ return $this->cache->get($key, 0);
+ }
+
+ /**
+ * Reset the number of attempts for the given key.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function resetAttempts($key)
+ {
+ $key = $this->cleanRateLimiterKey($key);
+
+ return $this->cache->forget($key);
+ }
+
+ /**
+ * Get the number of retries left for the given key.
+ *
+ * @param string $key
+ * @param int $maxAttempts
+ * @return int
+ */
+ public function remaining($key, $maxAttempts)
+ {
+ $key = $this->cleanRateLimiterKey($key);
+
+ $attempts = $this->attempts($key);
+
+ return $maxAttempts - $attempts;
+ }
+
+ /**
+ * Get the number of retries left for the given key.
+ *
+ * @param string $key
+ * @param int $maxAttempts
+ * @return int
+ */
+ public function retriesLeft($key, $maxAttempts)
+ {
+ return $this->remaining($key, $maxAttempts);
+ }
+
+ /**
+ * Clear the hits and lockout timer for the given key.
+ *
+ * @param string $key
+ * @return void
+ */
+ public function clear($key)
+ {
+ $key = $this->cleanRateLimiterKey($key);
+
+ $this->resetAttempts($key);
+
+ $this->cache->forget($key.':timer');
+ }
+
+ /**
+ * Get the number of seconds until the "key" is accessible again.
+ *
+ * @param string $key
+ * @return int
+ */
+ public function availableIn($key)
+ {
+ $key = $this->cleanRateLimiterKey($key);
+
+ return max(0, $this->cache->get($key.':timer') - $this->currentTime());
+ }
+
+ /**
+ * Clean the rate limiter key from unicode characters.
+ *
+ * @param string $key
+ * @return string
+ */
+ public function cleanRateLimiterKey($key)
+ {
+ return preg_replace('/&([a-z])[a-z]+;/i', '$1', htmlentities($key));
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/GlobalLimit.php b/vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/GlobalLimit.php
new file mode 100644
index 0000000..4f084eb
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/GlobalLimit.php
@@ -0,0 +1,18 @@
+key = $key;
+ $this->maxAttempts = $maxAttempts;
+ $this->decayMinutes = $decayMinutes;
+ }
+
+ /**
+ * Create a new rate limit.
+ *
+ * @param int $maxAttempts
+ * @return static
+ */
+ public static function perMinute($maxAttempts)
+ {
+ return new static('', $maxAttempts);
+ }
+
+ /**
+ * Create a new rate limit using minutes as decay time.
+ *
+ * @param int $decayMinutes
+ * @param int $maxAttempts
+ * @return static
+ */
+ public static function perMinutes($decayMinutes, $maxAttempts)
+ {
+ return new static('', $maxAttempts, $decayMinutes);
+ }
+
+ /**
+ * Create a new rate limit using hours as decay time.
+ *
+ * @param int $maxAttempts
+ * @param int $decayHours
+ * @return static
+ */
+ public static function perHour($maxAttempts, $decayHours = 1)
+ {
+ return new static('', $maxAttempts, 60 * $decayHours);
+ }
+
+ /**
+ * Create a new rate limit using days as decay time.
+ *
+ * @param int $maxAttempts
+ * @param int $decayDays
+ * @return static
+ */
+ public static function perDay($maxAttempts, $decayDays = 1)
+ {
+ return new static('', $maxAttempts, 60 * 24 * $decayDays);
+ }
+
+ /**
+ * Create a new unlimited rate limit.
+ *
+ * @return static
+ */
+ public static function none()
+ {
+ return new Unlimited;
+ }
+
+ /**
+ * Set the key of the rate limit.
+ *
+ * @param string $key
+ * @return $this
+ */
+ public function by($key)
+ {
+ $this->key = $key;
+
+ return $this;
+ }
+
+ /**
+ * Set the callback that should generate the response when the limit is exceeded.
+ *
+ * @param callable $callback
+ * @return $this
+ */
+ public function response(callable $callback)
+ {
+ $this->responseCallback = $callback;
+
+ return $this;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/Unlimited.php b/vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/Unlimited.php
new file mode 100644
index 0000000..fcfaa31
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/RateLimiting/Unlimited.php
@@ -0,0 +1,16 @@
+redis = $redis;
+ }
+
+ /**
+ * Attempt to acquire the lock.
+ *
+ * @return bool
+ */
+ public function acquire()
+ {
+ if ($this->seconds > 0) {
+ return $this->redis->set($this->name, $this->owner, 'EX', $this->seconds, 'NX') == true;
+ } else {
+ return $this->redis->setnx($this->name, $this->owner) === 1;
+ }
+ }
+
+ /**
+ * Release the lock.
+ *
+ * @return bool
+ */
+ public function release()
+ {
+ return (bool) $this->redis->eval(LuaScripts::releaseLock(), 1, $this->name, $this->owner);
+ }
+
+ /**
+ * Releases this lock in disregard of ownership.
+ *
+ * @return void
+ */
+ public function forceRelease()
+ {
+ $this->redis->del($this->name);
+ }
+
+ /**
+ * Returns the owner value written into the driver for this lock.
+ *
+ * @return string
+ */
+ protected function getCurrentOwner()
+ {
+ return $this->redis->get($this->name);
+ }
+
+ /**
+ * Get the name of the Redis connection being used to manage the lock.
+ *
+ * @return string
+ */
+ public function getConnectionName()
+ {
+ return $this->redis->getName();
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/RedisStore.php b/vendor/laravel/framework/src/Illuminate/Cache/RedisStore.php
new file mode 100755
index 0000000..4896c91
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/RedisStore.php
@@ -0,0 +1,347 @@
+redis = $redis;
+ $this->setPrefix($prefix);
+ $this->setConnection($connection);
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param string|array $key
+ * @return mixed
+ */
+ public function get($key)
+ {
+ $value = $this->connection()->get($this->prefix.$key);
+
+ return ! is_null($value) ? $this->unserialize($value) : null;
+ }
+
+ /**
+ * Retrieve multiple items from the cache by key.
+ *
+ * Items not found in the cache will have a null value.
+ *
+ * @param array $keys
+ * @return array
+ */
+ public function many(array $keys)
+ {
+ $results = [];
+
+ $values = $this->connection()->mget(array_map(function ($key) {
+ return $this->prefix.$key;
+ }, $keys));
+
+ foreach ($values as $index => $value) {
+ $results[$keys[$index]] = ! is_null($value) ? $this->unserialize($value) : null;
+ }
+
+ return $results;
+ }
+
+ /**
+ * Store an item in the cache for a given number of seconds.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function put($key, $value, $seconds)
+ {
+ return (bool) $this->connection()->setex(
+ $this->prefix.$key, (int) max(1, $seconds), $this->serialize($value)
+ );
+ }
+
+ /**
+ * Store multiple items in the cache for a given number of seconds.
+ *
+ * @param array $values
+ * @param int $seconds
+ * @return bool
+ */
+ public function putMany(array $values, $seconds)
+ {
+ $this->connection()->multi();
+
+ $manyResult = null;
+
+ foreach ($values as $key => $value) {
+ $result = $this->put($key, $value, $seconds);
+
+ $manyResult = is_null($manyResult) ? $result : $result && $manyResult;
+ }
+
+ $this->connection()->exec();
+
+ return $manyResult ?: false;
+ }
+
+ /**
+ * Store an item in the cache if the key doesn't exist.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param int $seconds
+ * @return bool
+ */
+ public function add($key, $value, $seconds)
+ {
+ $lua = "return redis.call('exists',KEYS[1])<1 and redis.call('setex',KEYS[1],ARGV[2],ARGV[1])";
+
+ return (bool) $this->connection()->eval(
+ $lua, 1, $this->prefix.$key, $this->serialize($value), (int) max(1, $seconds)
+ );
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int
+ */
+ public function increment($key, $value = 1)
+ {
+ return $this->connection()->incrby($this->prefix.$key, $value);
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->connection()->decrby($this->prefix.$key, $value);
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ return (bool) $this->connection()->set($this->prefix.$key, $this->serialize($value));
+ }
+
+ /**
+ * Get a lock instance.
+ *
+ * @param string $name
+ * @param int $seconds
+ * @param string|null $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function lock($name, $seconds = 0, $owner = null)
+ {
+ $lockName = $this->prefix.$name;
+
+ $lockConnection = $this->lockConnection();
+
+ if ($lockConnection instanceof PhpRedisConnection) {
+ return new PhpRedisLock($lockConnection, $lockName, $seconds, $owner);
+ }
+
+ return new RedisLock($lockConnection, $lockName, $seconds, $owner);
+ }
+
+ /**
+ * Restore a lock instance using the owner identifier.
+ *
+ * @param string $name
+ * @param string $owner
+ * @return \Illuminate\Contracts\Cache\Lock
+ */
+ public function restoreLock($name, $owner)
+ {
+ return $this->lock($name, 0, $owner);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ return (bool) $this->connection()->del($this->prefix.$key);
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ $this->connection()->flushdb();
+
+ return true;
+ }
+
+ /**
+ * Begin executing a new tags operation.
+ *
+ * @param array|mixed $names
+ * @return \Illuminate\Cache\RedisTaggedCache
+ */
+ public function tags($names)
+ {
+ return new RedisTaggedCache(
+ $this, new TagSet($this, is_array($names) ? $names : func_get_args())
+ );
+ }
+
+ /**
+ * Get the Redis connection instance.
+ *
+ * @return \Illuminate\Redis\Connections\Connection
+ */
+ public function connection()
+ {
+ return $this->redis->connection($this->connection);
+ }
+
+ /**
+ * Get the Redis connection instance that should be used to manage locks.
+ *
+ * @return \Illuminate\Redis\Connections\Connection
+ */
+ public function lockConnection()
+ {
+ return $this->redis->connection($this->lockConnection ?? $this->connection);
+ }
+
+ /**
+ * Specify the name of the connection that should be used to store data.
+ *
+ * @param string $connection
+ * @return void
+ */
+ public function setConnection($connection)
+ {
+ $this->connection = $connection;
+ }
+
+ /**
+ * Specify the name of the connection that should be used to manage locks.
+ *
+ * @param string $connection
+ * @return $this
+ */
+ public function setLockConnection($connection)
+ {
+ $this->lockConnection = $connection;
+
+ return $this;
+ }
+
+ /**
+ * Get the Redis database instance.
+ *
+ * @return \Illuminate\Contracts\Redis\Factory
+ */
+ public function getRedis()
+ {
+ return $this->redis;
+ }
+
+ /**
+ * Get the cache key prefix.
+ *
+ * @return string
+ */
+ public function getPrefix()
+ {
+ return $this->prefix;
+ }
+
+ /**
+ * Set the cache key prefix.
+ *
+ * @param string $prefix
+ * @return void
+ */
+ public function setPrefix($prefix)
+ {
+ $this->prefix = ! empty($prefix) ? $prefix.':' : '';
+ }
+
+ /**
+ * Serialize the value.
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function serialize($value)
+ {
+ return is_numeric($value) && ! in_array($value, [INF, -INF]) && ! is_nan($value) ? $value : serialize($value);
+ }
+
+ /**
+ * Unserialize the value.
+ *
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function unserialize($value)
+ {
+ return is_numeric($value) ? $value : unserialize($value);
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/RedisTaggedCache.php b/vendor/laravel/framework/src/Illuminate/Cache/RedisTaggedCache.php
new file mode 100644
index 0000000..7863dbc
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/RedisTaggedCache.php
@@ -0,0 +1,214 @@
+forever($key, $value);
+ }
+
+ $this->pushStandardKeys($this->tags->getNamespace(), $key);
+
+ return parent::put($key, $value, $ttl);
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function increment($key, $value = 1)
+ {
+ $this->pushStandardKeys($this->tags->getNamespace(), $key);
+
+ return parent::increment($key, $value);
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function decrement($key, $value = 1)
+ {
+ $this->pushStandardKeys($this->tags->getNamespace(), $key);
+
+ return parent::decrement($key, $value);
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ $this->pushForeverKeys($this->tags->getNamespace(), $key);
+
+ return parent::forever($key, $value);
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ $this->deleteForeverKeys();
+ $this->deleteStandardKeys();
+
+ $this->tags->flush();
+
+ return true;
+ }
+
+ /**
+ * Store standard key references into store.
+ *
+ * @param string $namespace
+ * @param string $key
+ * @return void
+ */
+ protected function pushStandardKeys($namespace, $key)
+ {
+ $this->pushKeys($namespace, $key, self::REFERENCE_KEY_STANDARD);
+ }
+
+ /**
+ * Store forever key references into store.
+ *
+ * @param string $namespace
+ * @param string $key
+ * @return void
+ */
+ protected function pushForeverKeys($namespace, $key)
+ {
+ $this->pushKeys($namespace, $key, self::REFERENCE_KEY_FOREVER);
+ }
+
+ /**
+ * Store a reference to the cache key against the reference key.
+ *
+ * @param string $namespace
+ * @param string $key
+ * @param string $reference
+ * @return void
+ */
+ protected function pushKeys($namespace, $key, $reference)
+ {
+ $fullKey = $this->store->getPrefix().sha1($namespace).':'.$key;
+
+ foreach (explode('|', $namespace) as $segment) {
+ $this->store->connection()->sadd($this->referenceKey($segment, $reference), $fullKey);
+ }
+ }
+
+ /**
+ * Delete all of the items that were stored forever.
+ *
+ * @return void
+ */
+ protected function deleteForeverKeys()
+ {
+ $this->deleteKeysByReference(self::REFERENCE_KEY_FOREVER);
+ }
+
+ /**
+ * Delete all standard items.
+ *
+ * @return void
+ */
+ protected function deleteStandardKeys()
+ {
+ $this->deleteKeysByReference(self::REFERENCE_KEY_STANDARD);
+ }
+
+ /**
+ * Find and delete all of the items that were stored against a reference.
+ *
+ * @param string $reference
+ * @return void
+ */
+ protected function deleteKeysByReference($reference)
+ {
+ foreach (explode('|', $this->tags->getNamespace()) as $segment) {
+ $this->deleteValues($segment = $this->referenceKey($segment, $reference));
+
+ $this->store->connection()->del($segment);
+ }
+ }
+
+ /**
+ * Delete item keys that have been stored against a reference.
+ *
+ * @param string $referenceKey
+ * @return void
+ */
+ protected function deleteValues($referenceKey)
+ {
+ $cursor = $defaultCursorValue = '0';
+
+ do {
+ [$cursor, $valuesChunk] = $this->store->connection()->sscan(
+ $referenceKey, $cursor, ['match' => '*', 'count' => 1000]
+ );
+
+ // PhpRedis client returns false if set does not exist or empty. Array destruction
+ // on false stores null in each variable. If valuesChunk is null, it means that
+ // there were not results from the previously executed "sscan" Redis command.
+ if (is_null($valuesChunk)) {
+ break;
+ }
+
+ $valuesChunk = array_unique($valuesChunk);
+
+ if (count($valuesChunk) > 0) {
+ $this->store->connection()->del(...$valuesChunk);
+ }
+ } while (((string) $cursor) !== $defaultCursorValue);
+ }
+
+ /**
+ * Get the reference key for the segment.
+ *
+ * @param string $segment
+ * @param string $suffix
+ * @return string
+ */
+ protected function referenceKey($segment, $suffix)
+ {
+ return $this->store->getPrefix().$segment.':'.$suffix;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/Repository.php b/vendor/laravel/framework/src/Illuminate/Cache/Repository.php
new file mode 100755
index 0000000..a9a1aa9
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/Repository.php
@@ -0,0 +1,680 @@
+store = $store;
+ }
+
+ /**
+ * Determine if an item exists in the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function has($key): bool
+ {
+ return ! is_null($this->get($key));
+ }
+
+ /**
+ * Determine if an item doesn't exist in the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function missing($key)
+ {
+ return ! $this->has($key);
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param array|string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($key, $default = null): mixed
+ {
+ if (is_array($key)) {
+ return $this->many($key);
+ }
+
+ $value = $this->store->get($this->itemKey($key));
+
+ // If we could not find the cache value, we will fire the missed event and get
+ // the default value for this cache value. This default could be a callback
+ // so we will execute the value function which will resolve it if needed.
+ if (is_null($value)) {
+ $this->event(new CacheMissed($key));
+
+ $value = value($default);
+ } else {
+ $this->event(new CacheHit($key, $value));
+ }
+
+ return $value;
+ }
+
+ /**
+ * Retrieve multiple items from the cache by key.
+ *
+ * Items not found in the cache will have a null value.
+ *
+ * @param array $keys
+ * @return array
+ */
+ public function many(array $keys)
+ {
+ $values = $this->store->many(collect($keys)->map(function ($value, $key) {
+ return is_string($key) ? $key : $value;
+ })->values()->all());
+
+ return collect($values)->map(function ($value, $key) use ($keys) {
+ return $this->handleManyResult($keys, $key, $value);
+ })->all();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return iterable
+ */
+ public function getMultiple($keys, $default = null): iterable
+ {
+ $defaults = [];
+
+ foreach ($keys as $key) {
+ $defaults[$key] = $default;
+ }
+
+ return $this->many($defaults);
+ }
+
+ /**
+ * Handle a result for the "many" method.
+ *
+ * @param array $keys
+ * @param string $key
+ * @param mixed $value
+ * @return mixed
+ */
+ protected function handleManyResult($keys, $key, $value)
+ {
+ // If we could not find the cache value, we will fire the missed event and get
+ // the default value for this cache value. This default could be a callback
+ // so we will execute the value function which will resolve it if needed.
+ if (is_null($value)) {
+ $this->event(new CacheMissed($key));
+
+ return isset($keys[$key]) ? value($keys[$key]) : null;
+ }
+
+ // If we found a valid value we will fire the "hit" event and return the value
+ // back from this function. The "hit" event gives developers an opportunity
+ // to listen for every possible cache "hit" throughout this applications.
+ $this->event(new CacheHit($key, $value));
+
+ return $value;
+ }
+
+ /**
+ * Retrieve an item from the cache and delete it.
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function pull($key, $default = null)
+ {
+ return tap($this->get($key, $default), function () use ($key) {
+ $this->forget($key);
+ });
+ }
+
+ /**
+ * Store an item in the cache.
+ *
+ * @param array|string $key
+ * @param mixed $value
+ * @param \DateTimeInterface|\DateInterval|int|null $ttl
+ * @return bool
+ */
+ public function put($key, $value, $ttl = null)
+ {
+ if (is_array($key)) {
+ return $this->putMany($key, $value);
+ }
+
+ if ($ttl === null) {
+ return $this->forever($key, $value);
+ }
+
+ $seconds = $this->getSeconds($ttl);
+
+ if ($seconds <= 0) {
+ return $this->forget($key);
+ }
+
+ $result = $this->store->put($this->itemKey($key), $value, $seconds);
+
+ if ($result) {
+ $this->event(new KeyWritten($key, $value, $seconds));
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function set($key, $value, $ttl = null): bool
+ {
+ return $this->put($key, $value, $ttl);
+ }
+
+ /**
+ * Store multiple items in the cache for a given number of seconds.
+ *
+ * @param array $values
+ * @param \DateTimeInterface|\DateInterval|int|null $ttl
+ * @return bool
+ */
+ public function putMany(array $values, $ttl = null)
+ {
+ if ($ttl === null) {
+ return $this->putManyForever($values);
+ }
+
+ $seconds = $this->getSeconds($ttl);
+
+ if ($seconds <= 0) {
+ return $this->deleteMultiple(array_keys($values));
+ }
+
+ $result = $this->store->putMany($values, $seconds);
+
+ if ($result) {
+ foreach ($values as $key => $value) {
+ $this->event(new KeyWritten($key, $value, $seconds));
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Store multiple items in the cache indefinitely.
+ *
+ * @param array $values
+ * @return bool
+ */
+ protected function putManyForever(array $values)
+ {
+ $result = true;
+
+ foreach ($values as $key => $value) {
+ if (! $this->forever($key, $value)) {
+ $result = false;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function setMultiple($values, $ttl = null): bool
+ {
+ return $this->putMany(is_array($values) ? $values : iterator_to_array($values), $ttl);
+ }
+
+ /**
+ * Store an item in the cache if the key does not exist.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @param \DateTimeInterface|\DateInterval|int|null $ttl
+ * @return bool
+ */
+ public function add($key, $value, $ttl = null)
+ {
+ $seconds = null;
+
+ if ($ttl !== null) {
+ $seconds = $this->getSeconds($ttl);
+
+ if ($seconds <= 0) {
+ return false;
+ }
+
+ // If the store has an "add" method we will call the method on the store so it
+ // has a chance to override this logic. Some drivers better support the way
+ // this operation should work with a total "atomic" implementation of it.
+ if (method_exists($this->store, 'add')) {
+ return $this->store->add(
+ $this->itemKey($key), $value, $seconds
+ );
+ }
+ }
+
+ // If the value did not exist in the cache, we will put the value in the cache
+ // so it exists for subsequent requests. Then, we will return true so it is
+ // easy to know if the value gets added. Otherwise, we will return false.
+ if (is_null($this->get($key))) {
+ return $this->put($key, $value, $seconds);
+ }
+
+ return false;
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function increment($key, $value = 1)
+ {
+ return $this->store->increment($key, $value);
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->store->decrement($key, $value);
+ }
+
+ /**
+ * Store an item in the cache indefinitely.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return bool
+ */
+ public function forever($key, $value)
+ {
+ $result = $this->store->forever($this->itemKey($key), $value);
+
+ if ($result) {
+ $this->event(new KeyWritten($key, $value));
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get an item from the cache, or execute the given Closure and store the result.
+ *
+ * @param string $key
+ * @param \Closure|\DateTimeInterface|\DateInterval|int|null $ttl
+ * @param \Closure $callback
+ * @return mixed
+ */
+ public function remember($key, $ttl, Closure $callback)
+ {
+ $value = $this->get($key);
+
+ // If the item exists in the cache we will just return this immediately and if
+ // not we will execute the given Closure and cache the result of that for a
+ // given number of seconds so it's available for all subsequent requests.
+ if (! is_null($value)) {
+ return $value;
+ }
+
+ $this->put($key, $value = $callback(), value($ttl));
+
+ return $value;
+ }
+
+ /**
+ * Get an item from the cache, or execute the given Closure and store the result forever.
+ *
+ * @param string $key
+ * @param \Closure $callback
+ * @return mixed
+ */
+ public function sear($key, Closure $callback)
+ {
+ return $this->rememberForever($key, $callback);
+ }
+
+ /**
+ * Get an item from the cache, or execute the given Closure and store the result forever.
+ *
+ * @param string $key
+ * @param \Closure $callback
+ * @return mixed
+ */
+ public function rememberForever($key, Closure $callback)
+ {
+ $value = $this->get($key);
+
+ // If the item exists in the cache we will just return this immediately
+ // and if not we will execute the given Closure and cache the result
+ // of that forever so it is available for all subsequent requests.
+ if (! is_null($value)) {
+ return $value;
+ }
+
+ $this->forever($key, $value = $callback());
+
+ return $value;
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function forget($key)
+ {
+ return tap($this->store->forget($this->itemKey($key)), function ($result) use ($key) {
+ if ($result) {
+ $this->event(new KeyForgotten($key));
+ }
+ });
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function delete($key): bool
+ {
+ return $this->forget($key);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function deleteMultiple($keys): bool
+ {
+ $result = true;
+
+ foreach ($keys as $key) {
+ if (! $this->forget($key)) {
+ $result = false;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return bool
+ */
+ public function clear(): bool
+ {
+ return $this->store->flush();
+ }
+
+ /**
+ * Begin executing a new tags operation if the store supports it.
+ *
+ * @param array|mixed $names
+ * @return \Illuminate\Cache\TaggedCache
+ *
+ * @throws \BadMethodCallException
+ */
+ public function tags($names)
+ {
+ if (! $this->supportsTags()) {
+ throw new BadMethodCallException('This cache store does not support tagging.');
+ }
+
+ $cache = $this->store->tags(is_array($names) ? $names : func_get_args());
+
+ if (! is_null($this->events)) {
+ $cache->setEventDispatcher($this->events);
+ }
+
+ return $cache->setDefaultCacheTime($this->default);
+ }
+
+ /**
+ * Format the key for a cache item.
+ *
+ * @param string $key
+ * @return string
+ */
+ protected function itemKey($key)
+ {
+ return $key;
+ }
+
+ /**
+ * Calculate the number of seconds for the given TTL.
+ *
+ * @param \DateTimeInterface|\DateInterval|int $ttl
+ * @return int
+ */
+ protected function getSeconds($ttl)
+ {
+ $duration = $this->parseDateInterval($ttl);
+
+ if ($duration instanceof DateTimeInterface) {
+ $duration = Carbon::now()->diffInRealSeconds($duration, false);
+ }
+
+ return (int) ($duration > 0 ? $duration : 0);
+ }
+
+ /**
+ * Determine if the current store supports tags.
+ *
+ * @return bool
+ */
+ public function supportsTags()
+ {
+ return method_exists($this->store, 'tags');
+ }
+
+ /**
+ * Get the default cache time.
+ *
+ * @return int|null
+ */
+ public function getDefaultCacheTime()
+ {
+ return $this->default;
+ }
+
+ /**
+ * Set the default cache time in seconds.
+ *
+ * @param int|null $seconds
+ * @return $this
+ */
+ public function setDefaultCacheTime($seconds)
+ {
+ $this->default = $seconds;
+
+ return $this;
+ }
+
+ /**
+ * Get the cache store implementation.
+ *
+ * @return \Illuminate\Contracts\Cache\Store
+ */
+ public function getStore()
+ {
+ return $this->store;
+ }
+
+ /**
+ * Fire an event for this cache instance.
+ *
+ * @param object|string $event
+ * @return void
+ */
+ protected function event($event)
+ {
+ $this->events?->dispatch($event);
+ }
+
+ /**
+ * Get the event dispatcher instance.
+ *
+ * @return \Illuminate\Contracts\Events\Dispatcher
+ */
+ public function getEventDispatcher()
+ {
+ return $this->events;
+ }
+
+ /**
+ * Set the event dispatcher instance.
+ *
+ * @param \Illuminate\Contracts\Events\Dispatcher $events
+ * @return void
+ */
+ public function setEventDispatcher(Dispatcher $events)
+ {
+ $this->events = $events;
+ }
+
+ /**
+ * Determine if a cached value exists.
+ *
+ * @param string $key
+ * @return bool
+ */
+ public function offsetExists($key): bool
+ {
+ return $this->has($key);
+ }
+
+ /**
+ * Retrieve an item from the cache by key.
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function offsetGet($key): mixed
+ {
+ return $this->get($key);
+ }
+
+ /**
+ * Store an item in the cache for the default time.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return void
+ */
+ public function offsetSet($key, $value): void
+ {
+ $this->put($key, $value, $this->default);
+ }
+
+ /**
+ * Remove an item from the cache.
+ *
+ * @param string $key
+ * @return void
+ */
+ public function offsetUnset($key): void
+ {
+ $this->forget($key);
+ }
+
+ /**
+ * Handle dynamic calls into macros or pass missing methods to the store.
+ *
+ * @param string $method
+ * @param array $parameters
+ * @return mixed
+ */
+ public function __call($method, $parameters)
+ {
+ if (static::hasMacro($method)) {
+ return $this->macroCall($method, $parameters);
+ }
+
+ return $this->store->$method(...$parameters);
+ }
+
+ /**
+ * Clone cache repository instance.
+ *
+ * @return void
+ */
+ public function __clone()
+ {
+ $this->store = clone $this->store;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/RetrievesMultipleKeys.php b/vendor/laravel/framework/src/Illuminate/Cache/RetrievesMultipleKeys.php
new file mode 100644
index 0000000..7db7a0a
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/RetrievesMultipleKeys.php
@@ -0,0 +1,49 @@
+mapWithKeys(function ($value, $key) {
+ return [is_string($key) ? $key : $value => is_string($key) ? $value : null];
+ })->all();
+
+ foreach ($keys as $key => $default) {
+ $return[$key] = $this->get($key, $default);
+ }
+
+ return $return;
+ }
+
+ /**
+ * Store multiple items in the cache for a given number of seconds.
+ *
+ * @param array $values
+ * @param int $seconds
+ * @return bool
+ */
+ public function putMany(array $values, $seconds)
+ {
+ $manyResult = null;
+
+ foreach ($values as $key => $value) {
+ $result = $this->put($key, $value, $seconds);
+
+ $manyResult = is_null($manyResult) ? $result : $result && $manyResult;
+ }
+
+ return $manyResult ?: false;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/TagSet.php b/vendor/laravel/framework/src/Illuminate/Cache/TagSet.php
new file mode 100644
index 0000000..471dc67
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/TagSet.php
@@ -0,0 +1,130 @@
+store = $store;
+ $this->names = $names;
+ }
+
+ /**
+ * Reset all tags in the set.
+ *
+ * @return void
+ */
+ public function reset()
+ {
+ array_walk($this->names, [$this, 'resetTag']);
+ }
+
+ /**
+ * Reset the tag and return the new tag identifier.
+ *
+ * @param string $name
+ * @return string
+ */
+ public function resetTag($name)
+ {
+ $this->store->forever($this->tagKey($name), $id = str_replace('.', '', uniqid('', true)));
+
+ return $id;
+ }
+
+ /**
+ * Flush all the tags in the set.
+ *
+ * @return void
+ */
+ public function flush()
+ {
+ array_walk($this->names, [$this, 'flushTag']);
+ }
+
+ /**
+ * Flush the tag from the cache.
+ *
+ * @param string $name
+ */
+ public function flushTag($name)
+ {
+ $this->store->forget($this->tagKey($name));
+ }
+
+ /**
+ * Get a unique namespace that changes when any of the tags are flushed.
+ *
+ * @return string
+ */
+ public function getNamespace()
+ {
+ return implode('|', $this->tagIds());
+ }
+
+ /**
+ * Get an array of tag identifiers for all of the tags in the set.
+ *
+ * @return array
+ */
+ protected function tagIds()
+ {
+ return array_map([$this, 'tagId'], $this->names);
+ }
+
+ /**
+ * Get the unique tag identifier for a given tag.
+ *
+ * @param string $name
+ * @return string
+ */
+ public function tagId($name)
+ {
+ return $this->store->get($this->tagKey($name)) ?: $this->resetTag($name);
+ }
+
+ /**
+ * Get the tag identifier key for a given tag.
+ *
+ * @param string $name
+ * @return string
+ */
+ public function tagKey($name)
+ {
+ return 'tag:'.$name.':key';
+ }
+
+ /**
+ * Get all of the tag names in the set.
+ *
+ * @return array
+ */
+ public function getNames()
+ {
+ return $this->names;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/TaggableStore.php b/vendor/laravel/framework/src/Illuminate/Cache/TaggableStore.php
new file mode 100644
index 0000000..6a12b45
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/TaggableStore.php
@@ -0,0 +1,19 @@
+tags = $tags;
+ }
+
+ /**
+ * Store multiple items in the cache for a given number of seconds.
+ *
+ * @param array $values
+ * @param int|null $ttl
+ * @return bool
+ */
+ public function putMany(array $values, $ttl = null)
+ {
+ if ($ttl === null) {
+ return $this->putManyForever($values);
+ }
+
+ return $this->putManyAlias($values, $ttl);
+ }
+
+ /**
+ * Increment the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function increment($key, $value = 1)
+ {
+ return $this->store->increment($this->itemKey($key), $value);
+ }
+
+ /**
+ * Decrement the value of an item in the cache.
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return int|bool
+ */
+ public function decrement($key, $value = 1)
+ {
+ return $this->store->decrement($this->itemKey($key), $value);
+ }
+
+ /**
+ * Remove all items from the cache.
+ *
+ * @return bool
+ */
+ public function flush()
+ {
+ $this->tags->reset();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function itemKey($key)
+ {
+ return $this->taggedItemKey($key);
+ }
+
+ /**
+ * Get a fully qualified key for a tagged item.
+ *
+ * @param string $key
+ * @return string
+ */
+ public function taggedItemKey($key)
+ {
+ return sha1($this->tags->getNamespace()).':'.$key;
+ }
+
+ /**
+ * Fire an event for this cache instance.
+ *
+ * @param \Illuminate\Cache\Events\CacheEvent $event
+ * @return void
+ */
+ protected function event($event)
+ {
+ parent::event($event->setTags($this->tags->getNames()));
+ }
+
+ /**
+ * Get the tag set instance.
+ *
+ * @return \Illuminate\Cache\TagSet
+ */
+ public function getTags()
+ {
+ return $this->tags;
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Cache/composer.json b/vendor/laravel/framework/src/Illuminate/Cache/composer.json
new file mode 100755
index 0000000..92dd4fb
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Cache/composer.json
@@ -0,0 +1,47 @@
+{
+ "name": "illuminate/cache",
+ "description": "The Illuminate Cache package.",
+ "license": "MIT",
+ "homepage": "https://laravel.com",
+ "support": {
+ "issues": "https://github.com/laravel/framework/issues",
+ "source": "https://github.com/laravel/framework"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^8.0.2",
+ "illuminate/collections": "^9.0",
+ "illuminate/contracts": "^9.0",
+ "illuminate/macroable": "^9.0",
+ "illuminate/support": "^9.0"
+ },
+ "provide": {
+ "psr/simple-cache-implementation": "1.0|2.0|3.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Illuminate\\Cache\\": ""
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "suggest": {
+ "ext-memcached": "Required to use the memcache cache driver.",
+ "illuminate/database": "Required to use the database cache driver (^9.0).",
+ "illuminate/filesystem": "Required to use the file cache driver (^9.0).",
+ "illuminate/redis": "Required to use the redis cache driver (^9.0).",
+ "symfony/cache": "Required to use PSR-6 cache bridge (^6.0)."
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "minimum-stability": "dev"
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Collections/Arr.php b/vendor/laravel/framework/src/Illuminate/Collections/Arr.php
new file mode 100644
index 0000000..afda8de
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Collections/Arr.php
@@ -0,0 +1,825 @@
+all();
+ } elseif (! is_array($values)) {
+ continue;
+ }
+
+ $results[] = $values;
+ }
+
+ return array_merge([], ...$results);
+ }
+
+ /**
+ * Cross join the given arrays, returning all possible permutations.
+ *
+ * @param iterable ...$arrays
+ * @return array
+ */
+ public static function crossJoin(...$arrays)
+ {
+ $results = [[]];
+
+ foreach ($arrays as $index => $array) {
+ $append = [];
+
+ foreach ($results as $product) {
+ foreach ($array as $item) {
+ $product[$index] = $item;
+
+ $append[] = $product;
+ }
+ }
+
+ $results = $append;
+ }
+
+ return $results;
+ }
+
+ /**
+ * Divide an array into two arrays. One with keys and the other with values.
+ *
+ * @param array $array
+ * @return array
+ */
+ public static function divide($array)
+ {
+ return [array_keys($array), array_values($array)];
+ }
+
+ /**
+ * Flatten a multi-dimensional associative array with dots.
+ *
+ * @param iterable $array
+ * @param string $prepend
+ * @return array
+ */
+ public static function dot($array, $prepend = '')
+ {
+ $results = [];
+
+ foreach ($array as $key => $value) {
+ if (is_array($value) && ! empty($value)) {
+ $results = array_merge($results, static::dot($value, $prepend.$key.'.'));
+ } else {
+ $results[$prepend.$key] = $value;
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Convert a flatten "dot" notation array into an expanded array.
+ *
+ * @param iterable $array
+ * @return array
+ */
+ public static function undot($array)
+ {
+ $results = [];
+
+ foreach ($array as $key => $value) {
+ static::set($results, $key, $value);
+ }
+
+ return $results;
+ }
+
+ /**
+ * Get all of the given array except for a specified array of keys.
+ *
+ * @param array $array
+ * @param array|string|int|float $keys
+ * @return array
+ */
+ public static function except($array, $keys)
+ {
+ static::forget($array, $keys);
+
+ return $array;
+ }
+
+ /**
+ * Determine if the given key exists in the provided array.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|int $key
+ * @return bool
+ */
+ public static function exists($array, $key)
+ {
+ if ($array instanceof Enumerable) {
+ return $array->has($key);
+ }
+
+ if ($array instanceof ArrayAccess) {
+ return $array->offsetExists($key);
+ }
+
+ if (is_float($key)) {
+ $key = (string) $key;
+ }
+
+ return array_key_exists($key, $array);
+ }
+
+ /**
+ * Return the first element in an array passing a given truth test.
+ *
+ * @param iterable $array
+ * @param callable|null $callback
+ * @param mixed $default
+ * @return mixed
+ */
+ public static function first($array, callable $callback = null, $default = null)
+ {
+ if (is_null($callback)) {
+ if (empty($array)) {
+ return value($default);
+ }
+
+ foreach ($array as $item) {
+ return $item;
+ }
+ }
+
+ foreach ($array as $key => $value) {
+ if ($callback($value, $key)) {
+ return $value;
+ }
+ }
+
+ return value($default);
+ }
+
+ /**
+ * Return the last element in an array passing a given truth test.
+ *
+ * @param array $array
+ * @param callable|null $callback
+ * @param mixed $default
+ * @return mixed
+ */
+ public static function last($array, callable $callback = null, $default = null)
+ {
+ if (is_null($callback)) {
+ return empty($array) ? value($default) : end($array);
+ }
+
+ return static::first(array_reverse($array, true), $callback, $default);
+ }
+
+ /**
+ * Flatten a multi-dimensional array into a single level.
+ *
+ * @param iterable $array
+ * @param int $depth
+ * @return array
+ */
+ public static function flatten($array, $depth = INF)
+ {
+ $result = [];
+
+ foreach ($array as $item) {
+ $item = $item instanceof Collection ? $item->all() : $item;
+
+ if (! is_array($item)) {
+ $result[] = $item;
+ } else {
+ $values = $depth === 1
+ ? array_values($item)
+ : static::flatten($item, $depth - 1);
+
+ foreach ($values as $value) {
+ $result[] = $value;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Remove one or many array items from a given array using "dot" notation.
+ *
+ * @param array $array
+ * @param array|string|int|float $keys
+ * @return void
+ */
+ public static function forget(&$array, $keys)
+ {
+ $original = &$array;
+
+ $keys = (array) $keys;
+
+ if (count($keys) === 0) {
+ return;
+ }
+
+ foreach ($keys as $key) {
+ // if the exact key exists in the top-level, remove it
+ if (static::exists($array, $key)) {
+ unset($array[$key]);
+
+ continue;
+ }
+
+ $parts = explode('.', $key);
+
+ // clean up before each pass
+ $array = &$original;
+
+ while (count($parts) > 1) {
+ $part = array_shift($parts);
+
+ if (isset($array[$part]) && static::accessible($array[$part])) {
+ $array = &$array[$part];
+ } else {
+ continue 2;
+ }
+ }
+
+ unset($array[array_shift($parts)]);
+ }
+ }
+
+ /**
+ * Get an item from an array using "dot" notation.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|int|null $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public static function get($array, $key, $default = null)
+ {
+ if (! static::accessible($array)) {
+ return value($default);
+ }
+
+ if (is_null($key)) {
+ return $array;
+ }
+
+ if (static::exists($array, $key)) {
+ return $array[$key];
+ }
+
+ if (! str_contains($key, '.')) {
+ return $array[$key] ?? value($default);
+ }
+
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($array) && static::exists($array, $segment)) {
+ $array = $array[$segment];
+ } else {
+ return value($default);
+ }
+ }
+
+ return $array;
+ }
+
+ /**
+ * Check if an item or items exist in an array using "dot" notation.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|array $keys
+ * @return bool
+ */
+ public static function has($array, $keys)
+ {
+ $keys = (array) $keys;
+
+ if (! $array || $keys === []) {
+ return false;
+ }
+
+ foreach ($keys as $key) {
+ $subKeyArray = $array;
+
+ if (static::exists($array, $key)) {
+ continue;
+ }
+
+ foreach (explode('.', $key) as $segment) {
+ if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) {
+ $subKeyArray = $subKeyArray[$segment];
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determine if any of the keys exist in an array using "dot" notation.
+ *
+ * @param \ArrayAccess|array $array
+ * @param string|array $keys
+ * @return bool
+ */
+ public static function hasAny($array, $keys)
+ {
+ if (is_null($keys)) {
+ return false;
+ }
+
+ $keys = (array) $keys;
+
+ if (! $array) {
+ return false;
+ }
+
+ if ($keys === []) {
+ return false;
+ }
+
+ foreach ($keys as $key) {
+ if (static::has($array, $key)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Determines if an array is associative.
+ *
+ * An array is "associative" if it doesn't have sequential numerical keys beginning with zero.
+ *
+ * @param array $array
+ * @return bool
+ */
+ public static function isAssoc(array $array)
+ {
+ $keys = array_keys($array);
+
+ return array_keys($keys) !== $keys;
+ }
+
+ /**
+ * Determines if an array is a list.
+ *
+ * An array is a "list" if all array keys are sequential integers starting from 0 with no gaps in between.
+ *
+ * @param array $array
+ * @return bool
+ */
+ public static function isList($array)
+ {
+ return ! self::isAssoc($array);
+ }
+
+ /**
+ * Join all items using a string. The final items can use a separate glue string.
+ *
+ * @param array $array
+ * @param string $glue
+ * @param string $finalGlue
+ * @return string
+ */
+ public static function join($array, $glue, $finalGlue = '')
+ {
+ if ($finalGlue === '') {
+ return implode($glue, $array);
+ }
+
+ if (count($array) === 0) {
+ return '';
+ }
+
+ if (count($array) === 1) {
+ return end($array);
+ }
+
+ $finalItem = array_pop($array);
+
+ return implode($glue, $array).$finalGlue.$finalItem;
+ }
+
+ /**
+ * Key an associative array by a field or using a callback.
+ *
+ * @param array $array
+ * @param callable|array|string $keyBy
+ * @return array
+ */
+ public static function keyBy($array, $keyBy)
+ {
+ return Collection::make($array)->keyBy($keyBy)->all();
+ }
+
+ /**
+ * Prepend the key names of an associative array.
+ *
+ * @param array $array
+ * @param string $prependWith
+ * @return array
+ */
+ public static function prependKeysWith($array, $prependWith)
+ {
+ return Collection::make($array)->mapWithKeys(function ($item, $key) use ($prependWith) {
+ return [$prependWith.$key => $item];
+ })->all();
+ }
+
+ /**
+ * Get a subset of the items from the given array.
+ *
+ * @param array $array
+ * @param array|string $keys
+ * @return array
+ */
+ public static function only($array, $keys)
+ {
+ return array_intersect_key($array, array_flip((array) $keys));
+ }
+
+ /**
+ * Pluck an array of values from an array.
+ *
+ * @param iterable $array
+ * @param string|array|int|null $value
+ * @param string|array|null $key
+ * @return array
+ */
+ public static function pluck($array, $value, $key = null)
+ {
+ $results = [];
+
+ [$value, $key] = static::explodePluckParameters($value, $key);
+
+ foreach ($array as $item) {
+ $itemValue = data_get($item, $value);
+
+ // If the key is "null", we will just append the value to the array and keep
+ // looping. Otherwise we will key the array using the value of the key we
+ // received from the developer. Then we'll return the final array form.
+ if (is_null($key)) {
+ $results[] = $itemValue;
+ } else {
+ $itemKey = data_get($item, $key);
+
+ if (is_object($itemKey) && method_exists($itemKey, '__toString')) {
+ $itemKey = (string) $itemKey;
+ }
+
+ $results[$itemKey] = $itemValue;
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Explode the "value" and "key" arguments passed to "pluck".
+ *
+ * @param string|array $value
+ * @param string|array|null $key
+ * @return array
+ */
+ protected static function explodePluckParameters($value, $key)
+ {
+ $value = is_string($value) ? explode('.', $value) : $value;
+
+ $key = is_null($key) || is_array($key) ? $key : explode('.', $key);
+
+ return [$value, $key];
+ }
+
+ /**
+ * Run a map over each of the items in the array.
+ *
+ * @param array $array
+ * @param callable $callback
+ * @return array
+ */
+ public static function map(array $array, callable $callback)
+ {
+ $keys = array_keys($array);
+
+ try {
+ $items = array_map($callback, $array, $keys);
+ } catch (ArgumentCountError) {
+ $items = array_map($callback, $array);
+ }
+
+ return array_combine($keys, $items);
+ }
+
+ /**
+ * Push an item onto the beginning of an array.
+ *
+ * @param array $array
+ * @param mixed $value
+ * @param mixed $key
+ * @return array
+ */
+ public static function prepend($array, $value, $key = null)
+ {
+ if (func_num_args() == 2) {
+ array_unshift($array, $value);
+ } else {
+ $array = [$key => $value] + $array;
+ }
+
+ return $array;
+ }
+
+ /**
+ * Get a value from the array, and remove it.
+ *
+ * @param array $array
+ * @param string|int $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public static function pull(&$array, $key, $default = null)
+ {
+ $value = static::get($array, $key, $default);
+
+ static::forget($array, $key);
+
+ return $value;
+ }
+
+ /**
+ * Convert the array into a query string.
+ *
+ * @param array $array
+ * @return string
+ */
+ public static function query($array)
+ {
+ return http_build_query($array, '', '&', PHP_QUERY_RFC3986);
+ }
+
+ /**
+ * Get one or a specified number of random values from an array.
+ *
+ * @param array $array
+ * @param int|null $number
+ * @param bool|false $preserveKeys
+ * @return mixed
+ *
+ * @throws \InvalidArgumentException
+ */
+ public static function random($array, $number = null, $preserveKeys = false)
+ {
+ $requested = is_null($number) ? 1 : $number;
+
+ $count = count($array);
+
+ if ($requested > $count) {
+ throw new InvalidArgumentException(
+ "You requested {$requested} items, but there are only {$count} items available."
+ );
+ }
+
+ if (is_null($number)) {
+ return $array[array_rand($array)];
+ }
+
+ if ((int) $number === 0) {
+ return [];
+ }
+
+ $keys = array_rand($array, $number);
+
+ $results = [];
+
+ if ($preserveKeys) {
+ foreach ((array) $keys as $key) {
+ $results[$key] = $array[$key];
+ }
+ } else {
+ foreach ((array) $keys as $key) {
+ $results[] = $array[$key];
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Set an array item to a given value using "dot" notation.
+ *
+ * If no key is given to the method, the entire array will be replaced.
+ *
+ * @param array $array
+ * @param string|int|null $key
+ * @param mixed $value
+ * @return array
+ */
+ public static function set(&$array, $key, $value)
+ {
+ if (is_null($key)) {
+ return $array = $value;
+ }
+
+ $keys = explode('.', $key);
+
+ foreach ($keys as $i => $key) {
+ if (count($keys) === 1) {
+ break;
+ }
+
+ unset($keys[$i]);
+
+ // If the key doesn't exist at this depth, we will just create an empty array
+ // to hold the next value, allowing us to create the arrays to hold final
+ // values at the correct depth. Then we'll keep digging into the array.
+ if (! isset($array[$key]) || ! is_array($array[$key])) {
+ $array[$key] = [];
+ }
+
+ $array = &$array[$key];
+ }
+
+ $array[array_shift($keys)] = $value;
+
+ return $array;
+ }
+
+ /**
+ * Shuffle the given array and return the result.
+ *
+ * @param array $array
+ * @param int|null $seed
+ * @return array
+ */
+ public static function shuffle($array, $seed = null)
+ {
+ if (is_null($seed)) {
+ shuffle($array);
+ } else {
+ mt_srand($seed);
+ shuffle($array);
+ mt_srand();
+ }
+
+ return $array;
+ }
+
+ /**
+ * Sort the array using the given callback or "dot" notation.
+ *
+ * @param array $array
+ * @param callable|array|string|null $callback
+ * @return array
+ */
+ public static function sort($array, $callback = null)
+ {
+ return Collection::make($array)->sortBy($callback)->all();
+ }
+
+ /**
+ * Recursively sort an array by keys and values.
+ *
+ * @param array $array
+ * @param int $options
+ * @param bool $descending
+ * @return array
+ */
+ public static function sortRecursive($array, $options = SORT_REGULAR, $descending = false)
+ {
+ foreach ($array as &$value) {
+ if (is_array($value)) {
+ $value = static::sortRecursive($value, $options, $descending);
+ }
+ }
+
+ if (static::isAssoc($array)) {
+ $descending
+ ? krsort($array, $options)
+ : ksort($array, $options);
+ } else {
+ $descending
+ ? rsort($array, $options)
+ : sort($array, $options);
+ }
+
+ return $array;
+ }
+
+ /**
+ * Conditionally compile classes from an array into a CSS class list.
+ *
+ * @param array $array
+ * @return string
+ */
+ public static function toCssClasses($array)
+ {
+ $classList = static::wrap($array);
+
+ $classes = [];
+
+ foreach ($classList as $class => $constraint) {
+ if (is_numeric($class)) {
+ $classes[] = $constraint;
+ } elseif ($constraint) {
+ $classes[] = $class;
+ }
+ }
+
+ return implode(' ', $classes);
+ }
+
+ /**
+ * Filter the array using the given callback.
+ *
+ * @param array $array
+ * @param callable $callback
+ * @return array
+ */
+ public static function where($array, callable $callback)
+ {
+ return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH);
+ }
+
+ /**
+ * Filter items where the value is not null.
+ *
+ * @param array $array
+ * @return array
+ */
+ public static function whereNotNull($array)
+ {
+ return static::where($array, function ($value) {
+ return ! is_null($value);
+ });
+ }
+
+ /**
+ * If the given value is not an array and not null, wrap it in one.
+ *
+ * @param mixed $value
+ * @return array
+ */
+ public static function wrap($value)
+ {
+ if (is_null($value)) {
+ return [];
+ }
+
+ return is_array($value) ? $value : [$value];
+ }
+}
diff --git a/vendor/laravel/framework/src/Illuminate/Collections/Collection.php b/vendor/laravel/framework/src/Illuminate/Collections/Collection.php
new file mode 100644
index 0000000..78cf76d
--- /dev/null
+++ b/vendor/laravel/framework/src/Illuminate/Collections/Collection.php
@@ -0,0 +1,1712 @@
+
+ * @implements \Illuminate\Support\Enumerable
+ */
+class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable
+{
+ /**
+ * @use \Illuminate\Support\Traits\EnumeratesValues
+ */
+ use EnumeratesValues, Macroable;
+
+ /**
+ * The items contained in the collection.
+ *
+ * @var array
+ */
+ protected $items = [];
+
+ /**
+ * Create a new collection.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items
+ * @return void
+ */
+ public function __construct($items = [])
+ {
+ $this->items = $this->getArrayableItems($items);
+ }
+
+ /**
+ * Create a collection with the given range.
+ *
+ * @param int $from
+ * @param int $to
+ * @return static
+ */
+ public static function range($from, $to)
+ {
+ return new static(range($from, $to));
+ }
+
+ /**
+ * Get all of the items in the collection.
+ *
+ * @return array
+ */
+ public function all()
+ {
+ return $this->items;
+ }
+
+ /**
+ * Get a lazy collection for the items in this collection.
+ *
+ * @return \Illuminate\Support\LazyCollection
+ */
+ public function lazy()
+ {
+ return new LazyCollection($this->items);
+ }
+
+ /**
+ * Get the average value of a given key.
+ *
+ * @param (callable(TValue): float|int)|string|null $callback
+ * @return float|int|null
+ */
+ public function avg($callback = null)
+ {
+ $callback = $this->valueRetriever($callback);
+
+ $items = $this->map(function ($value) use ($callback) {
+ return $callback($value);
+ })->filter(function ($value) {
+ return ! is_null($value);
+ });
+
+ if ($count = $items->count()) {
+ return $items->sum() / $count;
+ }
+ }
+
+ /**
+ * Get the median of a given key.
+ *
+ * @param string|array|null $key
+ * @return float|int|null
+ */
+ public function median($key = null)
+ {
+ $values = (isset($key) ? $this->pluck($key) : $this)
+ ->filter(function ($item) {
+ return ! is_null($item);
+ })->sort()->values();
+
+ $count = $values->count();
+
+ if ($count === 0) {
+ return;
+ }
+
+ $middle = (int) ($count / 2);
+
+ if ($count % 2) {
+ return $values->get($middle);
+ }
+
+ return (new static([
+ $values->get($middle - 1), $values->get($middle),
+ ]))->average();
+ }
+
+ /**
+ * Get the mode of a given key.
+ *
+ * @param string|array|null $key
+ * @return array|null
+ */
+ public function mode($key = null)
+ {
+ if ($this->count() === 0) {
+ return;
+ }
+
+ $collection = isset($key) ? $this->pluck($key) : $this;
+
+ $counts = new static;
+
+ $collection->each(function ($value) use ($counts) {
+ $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1;
+ });
+
+ $sorted = $counts->sort();
+
+ $highestValue = $sorted->last();
+
+ return $sorted->filter(function ($value) use ($highestValue) {
+ return $value == $highestValue;
+ })->sort()->keys()->all();
+ }
+
+ /**
+ * Collapse the collection of items into a single array.
+ *
+ * @return static
+ */
+ public function collapse()
+ {
+ return new static(Arr::collapse($this->items));
+ }
+
+ /**
+ * Determine if an item exists in the collection.
+ *
+ * @param (callable(TValue, TKey): bool)|TValue|string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function contains($key, $operator = null, $value = null)
+ {
+ if (func_num_args() === 1) {
+ if ($this->useAsCallable($key)) {
+ $placeholder = new stdClass;
+
+ return $this->first($key, $placeholder) !== $placeholder;
+ }
+
+ return in_array($key, $this->items);
+ }
+
+ return $this->contains($this->operatorForWhere(...func_get_args()));
+ }
+
+ /**
+ * Determine if an item is not contained in the collection.
+ *
+ * @param mixed $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return bool
+ */
+ public function doesntContain($key, $operator = null, $value = null)
+ {
+ return ! $this->contains(...func_get_args());
+ }
+
+ /**
+ * Cross join with the given lists, returning all possible permutations.
+ *
+ * @template TCrossJoinKey
+ * @template TCrossJoinValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists
+ * @return static>
+ */
+ public function crossJoin(...$lists)
+ {
+ return new static(Arr::crossJoin(
+ $this->items, ...array_map([$this, 'getArrayableItems'], $lists)
+ ));
+ }
+
+ /**
+ * Get the items in the collection that are not present in the given items.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function diff($items)
+ {
+ return new static(array_diff($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Get the items in the collection that are not present in the given items, using the callback.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @param callable(TValue, TValue): int $callback
+ * @return static
+ */
+ public function diffUsing($items, callable $callback)
+ {
+ return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback));
+ }
+
+ /**
+ * Get the items in the collection whose keys and values are not present in the given items.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function diffAssoc($items)
+ {
+ return new static(array_diff_assoc($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Get the items in the collection whose keys and values are not present in the given items, using the callback.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @param callable(TKey, TKey): int $callback
+ * @return static
+ */
+ public function diffAssocUsing($items, callable $callback)
+ {
+ return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback));
+ }
+
+ /**
+ * Get the items in the collection whose keys are not present in the given items.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function diffKeys($items)
+ {
+ return new static(array_diff_key($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Get the items in the collection whose keys are not present in the given items, using the callback.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @param callable(TKey, TKey): int $callback
+ * @return static
+ */
+ public function diffKeysUsing($items, callable $callback)
+ {
+ return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback));
+ }
+
+ /**
+ * Retrieve duplicate items from the collection.
+ *
+ * @param (callable(TValue): bool)|string|null $callback
+ * @param bool $strict
+ * @return static
+ */
+ public function duplicates($callback = null, $strict = false)
+ {
+ $items = $this->map($this->valueRetriever($callback));
+
+ $uniqueItems = $items->unique(null, $strict);
+
+ $compare = $this->duplicateComparator($strict);
+
+ $duplicates = new static;
+
+ foreach ($items as $key => $value) {
+ if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) {
+ $uniqueItems->shift();
+ } else {
+ $duplicates[$key] = $value;
+ }
+ }
+
+ return $duplicates;
+ }
+
+ /**
+ * Retrieve duplicate items from the collection using strict comparison.
+ *
+ * @param (callable(TValue): bool)|string|null $callback
+ * @return static
+ */
+ public function duplicatesStrict($callback = null)
+ {
+ return $this->duplicates($callback, true);
+ }
+
+ /**
+ * Get the comparison function to detect duplicates.
+ *
+ * @param bool $strict
+ * @return callable(TValue, TValue): bool
+ */
+ protected function duplicateComparator($strict)
+ {
+ if ($strict) {
+ return function ($a, $b) {
+ return $a === $b;
+ };
+ }
+
+ return function ($a, $b) {
+ return $a == $b;
+ };
+ }
+
+ /**
+ * Get all items except for those with the specified keys.
+ *
+ * @param \Illuminate\Support\Enumerable|array $keys
+ * @return static
+ */
+ public function except($keys)
+ {
+ if ($keys instanceof Enumerable) {
+ $keys = $keys->all();
+ } elseif (! is_array($keys)) {
+ $keys = func_get_args();
+ }
+
+ return new static(Arr::except($this->items, $keys));
+ }
+
+ /**
+ * Run a filter over each of the items.
+ *
+ * @param (callable(TValue, TKey): bool)|null $callback
+ * @return static
+ */
+ public function filter(callable $callback = null)
+ {
+ if ($callback) {
+ return new static(Arr::where($this->items, $callback));
+ }
+
+ return new static(array_filter($this->items));
+ }
+
+ /**
+ * Get the first item from the collection passing the given truth test.
+ *
+ * @template TFirstDefault
+ *
+ * @param (callable(TValue, TKey): bool)|null $callback
+ * @param TFirstDefault|(\Closure(): TFirstDefault) $default
+ * @return TValue|TFirstDefault
+ */
+ public function first(callable $callback = null, $default = null)
+ {
+ return Arr::first($this->items, $callback, $default);
+ }
+
+ /**
+ * Get a flattened array of the items in the collection.
+ *
+ * @param int $depth
+ * @return static
+ */
+ public function flatten($depth = INF)
+ {
+ return new static(Arr::flatten($this->items, $depth));
+ }
+
+ /**
+ * Flip the items in the collection.
+ *
+ * @return static
+ */
+ public function flip()
+ {
+ return new static(array_flip($this->items));
+ }
+
+ /**
+ * Remove an item from the collection by key.
+ *
+ * @param TKey|array $keys
+ * @return $this
+ */
+ public function forget($keys)
+ {
+ foreach ((array) $keys as $key) {
+ $this->offsetUnset($key);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get an item from the collection by key.
+ *
+ * @template TGetDefault
+ *
+ * @param TKey $key
+ * @param TGetDefault|(\Closure(): TGetDefault) $default
+ * @return TValue|TGetDefault
+ */
+ public function get($key, $default = null)
+ {
+ if (array_key_exists($key, $this->items)) {
+ return $this->items[$key];
+ }
+
+ return value($default);
+ }
+
+ /**
+ * Get an item from the collection by key or add it to collection if it does not exist.
+ *
+ * @param mixed $key
+ * @param mixed $value
+ * @return mixed
+ */
+ public function getOrPut($key, $value)
+ {
+ if (array_key_exists($key, $this->items)) {
+ return $this->items[$key];
+ }
+
+ $this->offsetSet($key, $value = value($value));
+
+ return $value;
+ }
+
+ /**
+ * Group an associative array by a field or using a callback.
+ *
+ * @param (callable(TValue, TKey): array-key)|array|string $groupBy
+ * @param bool $preserveKeys
+ * @return static>
+ */
+ public function groupBy($groupBy, $preserveKeys = false)
+ {
+ if (! $this->useAsCallable($groupBy) && is_array($groupBy)) {
+ $nextGroups = $groupBy;
+
+ $groupBy = array_shift($nextGroups);
+ }
+
+ $groupBy = $this->valueRetriever($groupBy);
+
+ $results = [];
+
+ foreach ($this->items as $key => $value) {
+ $groupKeys = $groupBy($value, $key);
+
+ if (! is_array($groupKeys)) {
+ $groupKeys = [$groupKeys];
+ }
+
+ foreach ($groupKeys as $groupKey) {
+ $groupKey = match (true) {
+ is_bool($groupKey) => (int) $groupKey,
+ $groupKey instanceof \Stringable => (string) $groupKey,
+ default => $groupKey,
+ };
+
+ if (! array_key_exists($groupKey, $results)) {
+ $results[$groupKey] = new static;
+ }
+
+ $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value);
+ }
+ }
+
+ $result = new static($results);
+
+ if (! empty($nextGroups)) {
+ return $result->map->groupBy($nextGroups, $preserveKeys);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Key an associative array by a field or using a callback.
+ *
+ * @param (callable(TValue, TKey): array-key)|array|string $keyBy
+ * @return static
+ */
+ public function keyBy($keyBy)
+ {
+ $keyBy = $this->valueRetriever($keyBy);
+
+ $results = [];
+
+ foreach ($this->items as $key => $item) {
+ $resolvedKey = $keyBy($item, $key);
+
+ if (is_object($resolvedKey)) {
+ $resolvedKey = (string) $resolvedKey;
+ }
+
+ $results[$resolvedKey] = $item;
+ }
+
+ return new static($results);
+ }
+
+ /**
+ * Determine if an item exists in the collection by key.
+ *
+ * @param TKey|array $key
+ * @return bool
+ */
+ public function has($key)
+ {
+ $keys = is_array($key) ? $key : func_get_args();
+
+ foreach ($keys as $value) {
+ if (! array_key_exists($value, $this->items)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Determine if any of the keys exist in the collection.
+ *
+ * @param mixed $key
+ * @return bool
+ */
+ public function hasAny($key)
+ {
+ if ($this->isEmpty()) {
+ return false;
+ }
+
+ $keys = is_array($key) ? $key : func_get_args();
+
+ foreach ($keys as $value) {
+ if ($this->has($value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Concatenate values of a given key as a string.
+ *
+ * @param callable|string $value
+ * @param string|null $glue
+ * @return string
+ */
+ public function implode($value, $glue = null)
+ {
+ if ($this->useAsCallable($value)) {
+ return implode($glue ?? '', $this->map($value)->all());
+ }
+
+ $first = $this->first();
+
+ if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) {
+ return implode($glue ?? '', $this->pluck($value)->all());
+ }
+
+ return implode($value ?? '', $this->items);
+ }
+
+ /**
+ * Intersect the collection with the given items.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function intersect($items)
+ {
+ return new static(array_intersect($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Intersect the collection with the given items by key.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function intersectByKeys($items)
+ {
+ return new static(array_intersect_key(
+ $this->items, $this->getArrayableItems($items)
+ ));
+ }
+
+ /**
+ * Determine if the collection is empty or not.
+ *
+ * @return bool
+ */
+ public function isEmpty()
+ {
+ return empty($this->items);
+ }
+
+ /**
+ * Determine if the collection contains a single item.
+ *
+ * @return bool
+ */
+ public function containsOneItem()
+ {
+ return $this->count() === 1;
+ }
+
+ /**
+ * Join all items from the collection using a string. The final items can use a separate glue string.
+ *
+ * @param string $glue
+ * @param string $finalGlue
+ * @return string
+ */
+ public function join($glue, $finalGlue = '')
+ {
+ if ($finalGlue === '') {
+ return $this->implode($glue);
+ }
+
+ $count = $this->count();
+
+ if ($count === 0) {
+ return '';
+ }
+
+ if ($count === 1) {
+ return $this->last();
+ }
+
+ $collection = new static($this->items);
+
+ $finalItem = $collection->pop();
+
+ return $collection->implode($glue).$finalGlue.$finalItem;
+ }
+
+ /**
+ * Get the keys of the collection items.
+ *
+ * @return static
+ */
+ public function keys()
+ {
+ return new static(array_keys($this->items));
+ }
+
+ /**
+ * Get the last item from the collection.
+ *
+ * @template TLastDefault
+ *
+ * @param (callable(TValue, TKey): bool)|null $callback
+ * @param TLastDefault|(\Closure(): TLastDefault) $default
+ * @return TValue|TLastDefault
+ */
+ public function last(callable $callback = null, $default = null)
+ {
+ return Arr::last($this->items, $callback, $default);
+ }
+
+ /**
+ * Get the values of a given key.
+ *
+ * @param string|int|array $value
+ * @param string|null $key
+ * @return static
+ */
+ public function pluck($value, $key = null)
+ {
+ return new static(Arr::pluck($this->items, $value, $key));
+ }
+
+ /**
+ * Run a map over each of the items.
+ *
+ * @template TMapValue
+ *
+ * @param callable(TValue, TKey): TMapValue $callback
+ * @return static
+ */
+ public function map(callable $callback)
+ {
+ return new static(Arr::map($this->items, $callback));
+ }
+
+ /**
+ * Run a dictionary map over the items.
+ *
+ * The callback should return an associative array with a single key/value pair.
+ *
+ * @template TMapToDictionaryKey of array-key
+ * @template TMapToDictionaryValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static>
+ */
+ public function mapToDictionary(callable $callback)
+ {
+ $dictionary = [];
+
+ foreach ($this->items as $key => $item) {
+ $pair = $callback($item, $key);
+
+ $key = key($pair);
+
+ $value = reset($pair);
+
+ if (! isset($dictionary[$key])) {
+ $dictionary[$key] = [];
+ }
+
+ $dictionary[$key][] = $value;
+ }
+
+ return new static($dictionary);
+ }
+
+ /**
+ * Run an associative map over each of the items.
+ *
+ * The callback should return an associative array with a single key/value pair.
+ *
+ * @template TMapWithKeysKey of array-key
+ * @template TMapWithKeysValue
+ *
+ * @param callable(TValue, TKey): array $callback
+ * @return static
+ */
+ public function mapWithKeys(callable $callback)
+ {
+ $result = [];
+
+ foreach ($this->items as $key => $value) {
+ $assoc = $callback($value, $key);
+
+ foreach ($assoc as $mapKey => $mapValue) {
+ $result[$mapKey] = $mapValue;
+ }
+ }
+
+ return new static($result);
+ }
+
+ /**
+ * Merge the collection with the given items.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function merge($items)
+ {
+ return new static(array_merge($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Recursively merge the collection with the given items.
+ *
+ * @template TMergeRecursiveValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function mergeRecursive($items)
+ {
+ return new static(array_merge_recursive($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Create a collection by using this collection for keys and another for its values.
+ *
+ * @template TCombineValue
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
+ * @return static
+ */
+ public function combine($values)
+ {
+ return new static(array_combine($this->all(), $this->getArrayableItems($values)));
+ }
+
+ /**
+ * Union the collection with the given items.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function union($items)
+ {
+ return new static($this->items + $this->getArrayableItems($items));
+ }
+
+ /**
+ * Create a new collection consisting of every n-th element.
+ *
+ * @param int $step
+ * @param int $offset
+ * @return static
+ */
+ public function nth($step, $offset = 0)
+ {
+ $new = [];
+
+ $position = 0;
+
+ foreach ($this->slice($offset)->items as $item) {
+ if ($position % $step === 0) {
+ $new[] = $item;
+ }
+
+ $position++;
+ }
+
+ return new static($new);
+ }
+
+ /**
+ * Get the items with the specified keys.
+ *
+ * @param \Illuminate\Support\Enumerable|array|string $keys
+ * @return static
+ */
+ public function only($keys)
+ {
+ if (is_null($keys)) {
+ return new static($this->items);
+ }
+
+ if ($keys instanceof Enumerable) {
+ $keys = $keys->all();
+ }
+
+ $keys = is_array($keys) ? $keys : func_get_args();
+
+ return new static(Arr::only($this->items, $keys));
+ }
+
+ /**
+ * Get and remove the last N items from the collection.
+ *
+ * @param int $count
+ * @return static|TValue|null
+ */
+ public function pop($count = 1)
+ {
+ if ($count === 1) {
+ return array_pop($this->items);
+ }
+
+ if ($this->isEmpty()) {
+ return new static;
+ }
+
+ $results = [];
+
+ $collectionCount = $this->count();
+
+ foreach (range(1, min($count, $collectionCount)) as $item) {
+ array_push($results, array_pop($this->items));
+ }
+
+ return new static($results);
+ }
+
+ /**
+ * Push an item onto the beginning of the collection.
+ *
+ * @param TValue $value
+ * @param TKey $key
+ * @return $this
+ */
+ public function prepend($value, $key = null)
+ {
+ $this->items = Arr::prepend($this->items, ...func_get_args());
+
+ return $this;
+ }
+
+ /**
+ * Push one or more items onto the end of the collection.
+ *
+ * @param TValue ...$values
+ * @return $this
+ */
+ public function push(...$values)
+ {
+ foreach ($values as $value) {
+ $this->items[] = $value;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Push all of the given items onto the collection.
+ *
+ * @param iterable $source
+ * @return static
+ */
+ public function concat($source)
+ {
+ $result = new static($this);
+
+ foreach ($source as $item) {
+ $result->push($item);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get and remove an item from the collection.
+ *
+ * @template TPullDefault
+ *
+ * @param TKey $key
+ * @param TPullDefault|(\Closure(): TPullDefault) $default
+ * @return TValue|TPullDefault
+ */
+ public function pull($key, $default = null)
+ {
+ return Arr::pull($this->items, $key, $default);
+ }
+
+ /**
+ * Put an item in the collection by key.
+ *
+ * @param TKey $key
+ * @param TValue $value
+ * @return $this
+ */
+ public function put($key, $value)
+ {
+ $this->offsetSet($key, $value);
+
+ return $this;
+ }
+
+ /**
+ * Get one or a specified number of items randomly from the collection.
+ *
+ * @param (callable(TValue): int)|int|null $number
+ * @return static|TValue
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function random($number = null)
+ {
+ if (is_null($number)) {
+ return Arr::random($this->items);
+ }
+
+ if (is_callable($number)) {
+ return new static(Arr::random($this->items, $number($this)));
+ }
+
+ return new static(Arr::random($this->items, $number));
+ }
+
+ /**
+ * Replace the collection items with the given items.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function replace($items)
+ {
+ return new static(array_replace($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Recursively replace the collection items with the given items.
+ *
+ * @param \Illuminate\Contracts\Support\Arrayable|iterable $items
+ * @return static
+ */
+ public function replaceRecursive($items)
+ {
+ return new static(array_replace_recursive($this->items, $this->getArrayableItems($items)));
+ }
+
+ /**
+ * Reverse items order.
+ *
+ * @return static
+ */
+ public function reverse()
+ {
+ return new static(array_reverse($this->items, true));
+ }
+
+ /**
+ * Search the collection for a given value and return the corresponding key if successful.
+ *
+ * @param TValue|(callable(TValue,TKey): bool) $value
+ * @param bool $strict
+ * @return TKey|bool
+ */
+ public function search($value, $strict = false)
+ {
+ if (! $this->useAsCallable($value)) {
+ return array_search($value, $this->items, $strict);
+ }
+
+ foreach ($this->items as $key => $item) {
+ if ($value($item, $key)) {
+ return $key;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get and remove the first N items from the collection.
+ *
+ * @param int $count
+ * @return static|TValue|null
+ */
+ public function shift($count = 1)
+ {
+ if ($count === 1) {
+ return array_shift($this->items);
+ }
+
+ if ($this->isEmpty()) {
+ return new static;
+ }
+
+ $results = [];
+
+ $collectionCount = $this->count();
+
+ foreach (range(1, min($count, $collectionCount)) as $item) {
+ array_push($results, array_shift($this->items));
+ }
+
+ return new static($results);
+ }
+
+ /**
+ * Shuffle the items in the collection.
+ *
+ * @param int|null $seed
+ * @return static
+ */
+ public function shuffle($seed = null)
+ {
+ return new static(Arr::shuffle($this->items, $seed));
+ }
+
+ /**
+ * Create chunks representing a "sliding window" view of the items in the collection.
+ *
+ * @param int $size
+ * @param int $step
+ * @return static
+ */
+ public function sliding($size = 2, $step = 1)
+ {
+ $chunks = floor(($this->count() - $size) / $step) + 1;
+
+ return static::times($chunks, function ($number) use ($size, $step) {
+ return $this->slice(($number - 1) * $step, $size);
+ });
+ }
+
+ /**
+ * Skip the first {$count} items.
+ *
+ * @param int $count
+ * @return static
+ */
+ public function skip($count)
+ {
+ return $this->slice($count);
+ }
+
+ /**
+ * Skip items in the collection until the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function skipUntil($value)
+ {
+ return new static($this->lazy()->skipUntil($value)->all());
+ }
+
+ /**
+ * Skip items in the collection while the given condition is met.
+ *
+ * @param TValue|callable(TValue,TKey): bool $value
+ * @return static
+ */
+ public function skipWhile($value)
+ {
+ return new static($this->lazy()->skipWhile($value)->all());
+ }
+
+ /**
+ * Slice the underlying collection array.
+ *
+ * @param int $offset
+ * @param int|null $length
+ * @return static
+ */
+ public function slice($offset, $length = null)
+ {
+ return new static(array_slice($this->items, $offset, $length, true));
+ }
+
+ /**
+ * Split a collection into a certain number of groups.
+ *
+ * @param int $numberOfGroups
+ * @return static
+ */
+ public function split($numberOfGroups)
+ {
+ if ($this->isEmpty()) {
+ return new static;
+ }
+
+ $groups = new static;
+
+ $groupSize = floor($this->count() / $numberOfGroups);
+
+ $remain = $this->count() % $numberOfGroups;
+
+ $start = 0;
+
+ for ($i = 0; $i < $numberOfGroups; $i++) {
+ $size = $groupSize;
+
+ if ($i < $remain) {
+ $size++;
+ }
+
+ if ($size) {
+ $groups->push(new static(array_slice($this->items, $start, $size)));
+
+ $start += $size;
+ }
+ }
+
+ return $groups;
+ }
+
+ /**
+ * Split a collection into a certain number of groups, and fill the first groups completely.
+ *
+ * @param int $numberOfGroups
+ * @return static
+ */
+ public function splitIn($numberOfGroups)
+ {
+ return $this->chunk(ceil($this->count() / $numberOfGroups));
+ }
+
+ /**
+ * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception.
+ *
+ * @param (callable(TValue, TKey): bool)|string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return TValue
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ * @throws \Illuminate\Support\MultipleItemsFoundException
+ */
+ public function sole($key = null, $operator = null, $value = null)
+ {
+ $filter = func_num_args() > 1
+ ? $this->operatorForWhere(...func_get_args())
+ : $key;
+
+ $items = $this->unless($filter == null)->filter($filter);
+
+ $count = $items->count();
+
+ if ($count === 0) {
+ throw new ItemNotFoundException;
+ }
+
+ if ($count > 1) {
+ throw new MultipleItemsFoundException($count);
+ }
+
+ return $items->first();
+ }
+
+ /**
+ * Get the first item in the collection but throw an exception if no matching items exist.
+ *
+ * @param (callable(TValue, TKey): bool)|string $key
+ * @param mixed $operator
+ * @param mixed $value
+ * @return TValue
+ *
+ * @throws \Illuminate\Support\ItemNotFoundException
+ */
+ public function firstOrFail($key = null, $operator = null, $value = null)
+ {
+ $filter = func_num_args() > 1
+ ? $this->operatorForWhere(...func_get_args())
+ : $key;
+
+ $placeholder = new stdClass();
+
+ $item = $this->first($filter, $placeholder);
+
+ if ($item === $placeholder) {
+ throw new ItemNotFoundException;
+ }
+
+ return $item;
+ }
+
+ /**
+ * Chunk the collection into chunks of the given size.
+ *
+ * @param int $size
+ * @return static