From 078942b82365d54b2b9d64aaef14ec77a44d01c5 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 10:01:35 +0700 Subject: [PATCH 01/35] feat(contracts): swap Illuminate\Contracts for illuminate/contracts:^13 (task 4.1a) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wave 4.1 opening move. Fork's on-disk src/Illuminate/Contracts/ (45 files) is replaced by the real illuminate/contracts v13.32.0 package. Mechanism correction to MIGRATION-SPLIT-MAP.md §3a: the "copy-source-EARLY" premise (composer require rejected because the package is fork-provided) does NOT hold for contracts/collections/macroable/conditionable/reflection/pipeline — none of them are in fork's `replace` block (27 entries). Plain `composer require` works; only the shadowing on-disk tree needs deleting. Two fork concretes had to be converged onto the newer package interfaces: - Container: add $scopedInstances + scoped()/scopedIf() (v13 interface) - ResponseFactory: 10 throwaway glue methods (noContent, file, streamJson, streamDownload, eventStream, redirect*). Fork RF is deleted when illuminate/routing v13 swaps in, so these are minimal — missing deps (Js/StreamedEvent/StreamedResponseException/report) inlined, redirect* resolves 'redirect' lazily from the container. Verified: contracts resolve from vendor/illuminate/contracts (not src/), suite 1647 green / 3659 assertions. --- composer.json | 1 + src/Illuminate/Container/Container.php | 35 +++++++ .../Contracts/Auth/Authenticatable.php | 14 --- src/Illuminate/Contracts/Auth/Guard.php | 14 --- .../Contracts/Auth/UserProvider.php | 13 --- src/Illuminate/Contracts/Bus/Dispatcher.php | 16 --- .../Contracts/Bus/QueueingDispatcher.php | 10 -- src/Illuminate/Contracts/Cache/Factory.php | 8 -- src/Illuminate/Contracts/Cache/Repository.php | 22 ----- src/Illuminate/Contracts/Cache/Store.php | 18 ---- .../Contracts/Config/Repository.php | 13 --- .../Container/BindingResolutionException.php | 11 --- .../Container/CircularDependencyException.php | 11 --- .../Contracts/Container/Container.php | 31 ------ .../Container/ContextualBindingBuilder.php | 11 --- src/Illuminate/Contracts/Cookie/Factory.php | 8 -- .../Contracts/Cookie/QueueingFactory.php | 10 -- .../Contracts/Database/ModelIdentifier.php | 31 ------ .../Contracts/Encryption/DecryptException.php | 10 -- .../Contracts/Encryption/EncryptException.php | 10 -- .../Contracts/Encryption/Encrypter.php | 12 --- .../Contracts/Encryption/StringEncrypter.php | 26 ----- .../Contracts/Events/Dispatcher.php | 16 --- .../Filesystem/FileNotFoundException.php | 10 -- .../Contracts/Filesystem/Filesystem.php | 33 ------- src/Illuminate/Contracts/Hashing/Hasher.php | 11 --- src/Illuminate/Contracts/Http/Kernel.php | 11 --- src/Illuminate/Contracts/Mail/MailQueue.php | 9 -- src/Illuminate/Contracts/Mail/Mailer.php | 13 --- src/Illuminate/Contracts/Pipeline/Hub.php | 15 --- .../Contracts/Pipeline/Pipeline.php | 40 -------- src/Illuminate/Contracts/Queue/Job.php | 30 ------ src/Illuminate/Contracts/Queue/Queue.php | 21 ---- .../Contracts/Queue/ShouldQueue.php | 8 -- src/Illuminate/Contracts/Redis/Factory.php | 8 -- .../Contracts/Routing/Registrar.php | 18 ---- .../Contracts/Routing/ResponseFactory.php | 71 -------------- .../Contracts/Routing/UrlGenerator.php | 19 ---- src/Illuminate/Contracts/Session/Session.php | 34 ------- .../Contracts/Support/Arrayable.php | 8 -- .../Support/CanBeEscapedWhenCastToString.php | 14 --- .../Contracts/Support/DeferrableProvider.php | 13 --- src/Illuminate/Contracts/Support/Jsonable.php | 8 -- .../Contracts/Support/MessageProvider.php | 8 -- .../Contracts/Support/Renderable.php | 8 -- src/Illuminate/Contracts/View/Factory.php | 15 --- src/Illuminate/Contracts/View/View.php | 12 --- src/Illuminate/Routing/ResponseFactory.php | 98 +++++++++++++++++++ 48 files changed, 134 insertions(+), 752 deletions(-) delete mode 100644 src/Illuminate/Contracts/Auth/Authenticatable.php delete mode 100644 src/Illuminate/Contracts/Auth/Guard.php delete mode 100644 src/Illuminate/Contracts/Auth/UserProvider.php delete mode 100644 src/Illuminate/Contracts/Bus/Dispatcher.php delete mode 100644 src/Illuminate/Contracts/Bus/QueueingDispatcher.php delete mode 100644 src/Illuminate/Contracts/Cache/Factory.php delete mode 100644 src/Illuminate/Contracts/Cache/Repository.php delete mode 100644 src/Illuminate/Contracts/Cache/Store.php delete mode 100644 src/Illuminate/Contracts/Config/Repository.php delete mode 100644 src/Illuminate/Contracts/Container/BindingResolutionException.php delete mode 100644 src/Illuminate/Contracts/Container/CircularDependencyException.php delete mode 100644 src/Illuminate/Contracts/Container/Container.php delete mode 100644 src/Illuminate/Contracts/Container/ContextualBindingBuilder.php delete mode 100644 src/Illuminate/Contracts/Cookie/Factory.php delete mode 100644 src/Illuminate/Contracts/Cookie/QueueingFactory.php delete mode 100644 src/Illuminate/Contracts/Database/ModelIdentifier.php delete mode 100644 src/Illuminate/Contracts/Encryption/DecryptException.php delete mode 100644 src/Illuminate/Contracts/Encryption/EncryptException.php delete mode 100644 src/Illuminate/Contracts/Encryption/Encrypter.php delete mode 100644 src/Illuminate/Contracts/Encryption/StringEncrypter.php delete mode 100644 src/Illuminate/Contracts/Events/Dispatcher.php delete mode 100644 src/Illuminate/Contracts/Filesystem/FileNotFoundException.php delete mode 100644 src/Illuminate/Contracts/Filesystem/Filesystem.php delete mode 100644 src/Illuminate/Contracts/Hashing/Hasher.php delete mode 100644 src/Illuminate/Contracts/Http/Kernel.php delete mode 100644 src/Illuminate/Contracts/Mail/MailQueue.php delete mode 100644 src/Illuminate/Contracts/Mail/Mailer.php delete mode 100644 src/Illuminate/Contracts/Pipeline/Hub.php delete mode 100644 src/Illuminate/Contracts/Pipeline/Pipeline.php delete mode 100644 src/Illuminate/Contracts/Queue/Job.php delete mode 100644 src/Illuminate/Contracts/Queue/Queue.php delete mode 100644 src/Illuminate/Contracts/Queue/ShouldQueue.php delete mode 100644 src/Illuminate/Contracts/Redis/Factory.php delete mode 100644 src/Illuminate/Contracts/Routing/Registrar.php delete mode 100644 src/Illuminate/Contracts/Routing/ResponseFactory.php delete mode 100644 src/Illuminate/Contracts/Routing/UrlGenerator.php delete mode 100644 src/Illuminate/Contracts/Session/Session.php delete mode 100644 src/Illuminate/Contracts/Support/Arrayable.php delete mode 100644 src/Illuminate/Contracts/Support/CanBeEscapedWhenCastToString.php delete mode 100644 src/Illuminate/Contracts/Support/DeferrableProvider.php delete mode 100644 src/Illuminate/Contracts/Support/Jsonable.php delete mode 100644 src/Illuminate/Contracts/Support/MessageProvider.php delete mode 100644 src/Illuminate/Contracts/Support/Renderable.php delete mode 100644 src/Illuminate/Contracts/View/Factory.php delete mode 100644 src/Illuminate/Contracts/View/View.php diff --git a/composer.json b/composer.json index 4e23657d..177609dc 100755 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "php": ">=8.4.23", "d11wtq/boris": "~1.0", "filp/whoops": "~2.11", + "illuminate/contracts": "^13", "ircmaxell/password-compat": "~1.0", "laravel/serializable-closure": "^1.2", "monolog/monolog": "^3.10", diff --git a/src/Illuminate/Container/Container.php b/src/Illuminate/Container/Container.php index 61951bad..32351bce 100755 --- a/src/Illuminate/Container/Container.php +++ b/src/Illuminate/Container/Container.php @@ -38,6 +38,11 @@ class Container implements ArrayAccess, ContainerContract { */ protected array $instances = []; + /** + * The registered scoped instances. + */ + protected array $scopedInstances = []; + /** * The registered type aliases. */ @@ -375,6 +380,36 @@ public function singletonIf($abstract, $concrete = null): void } } + /** + * Register a scoped binding in the container. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function scoped($abstract, $concrete = null): void + { + if (! in_array($abstract, $this->scopedInstances, true)) { + $this->scopedInstances[] = $abstract; + } + + $this->singleton($abstract, $concrete); + } + + /** + * Register a scoped binding if it hasn't already been registered. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function scopedIf($abstract, $concrete = null): void + { + if (! $this->bound($abstract)) { + $this->scoped($abstract, $concrete); + } + } + /** * Wrap a Closure such that it is shared. * diff --git a/src/Illuminate/Contracts/Auth/Authenticatable.php b/src/Illuminate/Contracts/Auth/Authenticatable.php deleted file mode 100644 index 4e5a8006..00000000 --- a/src/Illuminate/Contracts/Auth/Authenticatable.php +++ /dev/null @@ -1,14 +0,0 @@ -class = $class; - $this->id = $id; - $this->relations = $relations; - $this->connection = $connection; - } - - public function useCollectionClass(?string $collectionClass) - { - $this->collectionClass = $collectionClass; - return $this; - } - - public function getClass(): ?string - { - return $this->class; - } -} diff --git a/src/Illuminate/Contracts/Encryption/DecryptException.php b/src/Illuminate/Contracts/Encryption/DecryptException.php deleted file mode 100644 index 7edebc9c..00000000 --- a/src/Illuminate/Contracts/Encryption/DecryptException.php +++ /dev/null @@ -1,10 +0,0 @@ -make('', $status, $headers); + } + + public function file($file, array $headers = []) + { + return new BinaryFileResponse($file, 200, $headers); + } + + public function streamJson($data, $status = 200, $headers = [], $encodingOptions = JsonResponse::DEFAULT_ENCODING_OPTIONS) + { + return new StreamedJsonResponse($data, $status, $headers, $encodingOptions); + } + + public function streamDownload($callback, $name = null, array $headers = [], $disposition = 'attachment') + { + $response = new StreamedResponse($callback, 200, $headers); + + if ( ! is_null($name)) + { + $response->headers->set('Content-Disposition', $response->headers->makeDisposition( + $disposition, $name, str_replace('%', '', Str::ascii($name)) + )); + } + + return $response; + } + + public function eventStream(Closure $callback, array $headers = [], $endStreamWith = '') + { + return $this->stream(function () use ($callback, $endStreamWith) { + foreach ($callback() as $message) + { + if (connection_aborted()) break; + + if ( ! is_string($message) && ! is_numeric($message)) + { + $message = json_encode($message); + } + + echo "event: update\n"; + echo 'data: '.$message; + echo "\n\n"; + + if (ob_get_level() > 0) ob_flush(); + flush(); + } + + if ($endStreamWith !== null && $endStreamWith !== '') + { + echo "event: update\n"; + echo 'data: '.$endStreamWith; + echo "\n\n"; + + if (ob_get_level() > 0) ob_flush(); + flush(); + } + }, 200, array_merge($headers, [ + 'Content-Type' => 'text/event-stream', + 'Cache-Control' => 'no-cache', + 'X-Accel-Buffering' => 'no', + ])); + } + + public function redirectTo($path, $status = 302, $headers = [], $secure = null) + { + return $this->container['redirect']->to($path, $status, $headers, $secure); + } + + public function redirectToRoute($route, $parameters = [], $status = 302, $headers = []) + { + return $this->container['redirect']->route($route, $parameters, $status, $headers); + } + + public function redirectToAction($action, $parameters = [], $status = 302, $headers = []) + { + return $this->container['redirect']->action($action, $parameters, $status, $headers); + } + + public function redirectGuest($path, $status = 302, $headers = [], $secure = null) + { + return $this->container['redirect']->guest($path, $status, $headers, $secure); + } + + public function redirectToIntended($default = '/', $status = 302, $headers = [], $secure = null) + { + return $this->container['redirect']->intended($default, $status, $headers, $secure); + } + } From 1845cf78d0af95fa3f0b9e5a830a2169c554505d Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 10:04:13 +0700 Subject: [PATCH 02/35] feat(support): swap Macroable/Conditionable for illuminate/*:^13 (task 4.1b) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second Wave 4.1 slice, same mechanism as 4.1a: neither package is in fork's `replace` block, so plain composer require works. illuminate/macroable + illuminate/conditionable both autoload "Illuminate\Support\" => "" — the same prefix fork uses for src/Illuminate/Support/. Composer resolves longest-prefix-first with fallback, so the packages only shadow what they actually ship. Verified: Traits\Macroable -> vendor/illuminate/macroable Traits\Conditionable -> vendor/illuminate/conditionable HigherOrderWhenProxy -> vendor/illuminate/conditionable Collection / Str / Reflector -> src/Illuminate/Support (fork, untouched) Three fork files are now dead (shadowed) and deleted. MacroableTrait.php is kept as the BC alias shim (drops at the flip). Suite 1647 green / 3659 assertions. --- composer.json | 2 + .../Support/HigherOrderWhenProxy.php | 108 -------------- .../Support/Traits/Conditionable.php | 73 ---------- src/Illuminate/Support/Traits/Macroable.php | 134 ------------------ 4 files changed, 2 insertions(+), 315 deletions(-) delete mode 100644 src/Illuminate/Support/HigherOrderWhenProxy.php delete mode 100644 src/Illuminate/Support/Traits/Conditionable.php delete mode 100644 src/Illuminate/Support/Traits/Macroable.php diff --git a/composer.json b/composer.json index 177609dc..10d1e38c 100755 --- a/composer.json +++ b/composer.json @@ -13,7 +13,9 @@ "php": ">=8.4.23", "d11wtq/boris": "~1.0", "filp/whoops": "~2.11", + "illuminate/conditionable": "^13", "illuminate/contracts": "^13", + "illuminate/macroable": "^13", "ircmaxell/password-compat": "~1.0", "laravel/serializable-closure": "^1.2", "monolog/monolog": "^3.10", diff --git a/src/Illuminate/Support/HigherOrderWhenProxy.php b/src/Illuminate/Support/HigherOrderWhenProxy.php deleted file mode 100644 index 0a694c24..00000000 --- a/src/Illuminate/Support/HigherOrderWhenProxy.php +++ /dev/null @@ -1,108 +0,0 @@ -target = $target; - } - - /** - * Set the condition on the proxy. - * - * @param bool $condition - * @return $this - */ - public function condition($condition) - { - [$this->condition, $this->hasCondition] = [$condition, true]; - - return $this; - } - - /** - * Indicate that the condition should be negated. - * - * @return $this - */ - public function negateConditionOnCapture() - { - $this->negateConditionOnCapture = true; - - return $this; - } - - /** - * Proxy accessing an attribute onto the target. - * - * @param string $key - * @return mixed - */ - public function __get($key) - { - if (! $this->hasCondition) { - $condition = $this->target->{$key}; - - return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition); - } - - return $this->condition - ? $this->target->{$key} - : $this->target; - } - - /** - * Proxy a method call on the target. - * - * @param string $method - * @param array $parameters - * @return mixed - */ - public function __call($method, $parameters) - { - if (! $this->hasCondition) { - $condition = $this->target->{$method}(...$parameters); - - return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition); - } - - return $this->condition - ? $this->target->{$method}(...$parameters) - : $this->target; - } -} diff --git a/src/Illuminate/Support/Traits/Conditionable.php b/src/Illuminate/Support/Traits/Conditionable.php deleted file mode 100644 index 5e3194bb..00000000 --- a/src/Illuminate/Support/Traits/Conditionable.php +++ /dev/null @@ -1,73 +0,0 @@ -condition($value); - } - - if ($value) { - return $callback($this, $value) ?? $this; - } elseif ($default) { - return $default($this, $value) ?? $this; - } - - return $this; - } - - /** - * Apply the callback if the given "value" is (or resolves to) falsy. - * - * @template TUnlessParameter - * @template TUnlessReturnType - * - * @param (\Closure($this): TUnlessParameter)|TUnlessParameter|null $value - * @param (callable($this, TUnlessParameter): TUnlessReturnType)|null $callback - * @param (callable($this, TUnlessParameter): TUnlessReturnType)|null $default - * @return $this|TUnlessReturnType - */ - public function unless($value = null, ?callable $callback = null, ?callable $default = null) - { - $value = $value instanceof Closure ? $value($this) : $value; - - if (func_num_args() === 0) { - return (new HigherOrderWhenProxy($this))->negateConditionOnCapture(); - } - - if (func_num_args() === 1) { - return (new HigherOrderWhenProxy($this))->condition(! $value); - } - - if (! $value) { - return $callback($this, $value) ?? $this; - } elseif ($default) { - return $default($this, $value) ?? $this; - } - - return $this; - } -} diff --git a/src/Illuminate/Support/Traits/Macroable.php b/src/Illuminate/Support/Traits/Macroable.php deleted file mode 100644 index 2ee06e17..00000000 --- a/src/Illuminate/Support/Traits/Macroable.php +++ /dev/null @@ -1,134 +0,0 @@ -getMethods( - ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED - ); - - foreach ($methods as $method) { - if ($replace || ! static::hasMacro($method->name)) { - static::macro($method->name, $method->invoke($mixin)); - } - } - } - - /** - * Checks if macro is registered. - * - * @param string $name - * @return bool - */ - public static function hasMacro($name) - { - return isset(static::$macros[$name]); - } - - /** - * Flush the existing macros. - * - * @return void - */ - public static function flushMacros() - { - static::$macros = []; - } - - /** - * Dynamically handle calls to the class. - * - * @param string $method - * @param array $parameters - * @return mixed - * - * @throws \BadMethodCallException - */ - public static function __callStatic($method, $parameters) - { - if (! static::hasMacro($method)) { - throw new BadMethodCallException(sprintf( - 'Method %s::%s does not exist.', static::class, $method - )); - } - - $macro = static::$macros[$method]; - - if ($macro instanceof Closure) { - $macro = $macro->bindTo(null, static::class); - } - - return $macro(...$parameters); - } - - /** - * Dynamically handle calls to the class. - * - * @param string $method - * @param array $parameters - * @return mixed - * - * @throws \BadMethodCallException - */ - public function __call($method, $parameters) - { - if (! static::hasMacro($method)) { - throw new BadMethodCallException(sprintf( - 'Method %s::%s does not exist.', static::class, $method - )); - } - - $macro = static::$macros[$method]; - - if ($macro instanceof Closure) { - try { - $macro = $macro->bindTo($this, static::class) ?? throw new RuntimeException; - } catch (Throwable) { - $macro = $macro->bindTo(null, static::class); - } - } - - return $macro(...$parameters); - } -} From 689a0f871a47497eb92452ff4064d50f3b34a3fa Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 10:13:52 +0700 Subject: [PATCH 03/35] feat(support): swap Collections for illuminate/collections:^13 (task 4.1c) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third Wave 4.1 slice. illuminate/collections autoloads "Illuminate\Support\" => "" plus files [functions.php, helpers.php], so it shadows exactly the 10 fork files it duplicates; the other 53 under src/Illuminate/Support survive (longest-prefix-first with fallback). Eight of those were classes and are deleted; helpers.php/functions.php are KEPT because 54 of the fork's 59 global helpers are fork-only — both sides guard every function with function_exists, so the 5 overlapping names (data_get, enum_value, head, last, value) resolve to the package's, which now load first. Task 3.3b had already converged Collection/Arr to L13 shape, so the swap came down to four 4.2-only APIs the package does not have. Swept every Arr:: call in fork src against v13's Arr (59 methods) — only build/fetch were missing: - array_build()/array_fetch() globals now inline their bodies instead of delegating to the removed Arr::build/Arr::fetch. Consumers are Routing\Route and Database\Eloquent\Collection, plus seven app-side test call sites of array_fetch. - Database\Query\Builder::pluck() now uses Collection::pluck (upstream's own implementation) instead of the removed Collection::fetch. - Eloquent\Collection::fetch() keeps its body (it rides the array_fetch global) but loses its #[\Override], which is now false since v13's base Collection dropped fetch(). Found by sweeping all 177 #[\Override] in fork src — 100 files clean, this was the only mismatch. Collection::lists() has zero callers in fork src and zero in the app, so the four tests covering it move to pluck()/->all() (names follow upstream). Two Arr tests covered v13-differing behaviour and were converged: Arr::exists on an Enumerable now goes through Collection::has (array_key_exists semantics), and Arr::shuffle lost its seed parameter so testShuffleWithSeed is dropped. The test stub TestAccessorEloquentTestStub gains the __isset upstream has. Suite 1646 green / 3658 assertions. --- composer.json | 1 + .../Database/Eloquent/Collection.php | 1 - src/Illuminate/Database/Query/Builder.php | 4 +- src/Illuminate/Support/Arr.php | 982 -------- src/Illuminate/Support/Collection.php | 2050 ----------------- src/Illuminate/Support/Enumerable.php | 1379 ----------- .../Support/HigherOrderCollectionProxy.php | 69 - .../Support/ItemNotFoundException.php | 9 - src/Illuminate/Support/LazyCollection.php | 1978 ---------------- .../Support/MultipleItemsFoundException.php | 39 - .../Support/Traits/EnumeratesValues.php | 1244 ---------- src/Illuminate/Support/helpers.php | 28 +- .../DatabaseEloquentCollectionTest.php | 6 +- tests/Support/SupportArrTest.php | 10 +- tests/Support/SupportCollectionTest.php | 25 +- 15 files changed, 52 insertions(+), 7773 deletions(-) delete mode 100755 src/Illuminate/Support/Arr.php delete mode 100755 src/Illuminate/Support/Collection.php delete mode 100644 src/Illuminate/Support/Enumerable.php delete mode 100644 src/Illuminate/Support/HigherOrderCollectionProxy.php delete mode 100644 src/Illuminate/Support/ItemNotFoundException.php delete mode 100644 src/Illuminate/Support/LazyCollection.php delete mode 100644 src/Illuminate/Support/MultipleItemsFoundException.php delete mode 100644 src/Illuminate/Support/Traits/EnumeratesValues.php diff --git a/composer.json b/composer.json index 10d1e38c..70497caf 100755 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "php": ">=8.4.23", "d11wtq/boris": "~1.0", "filp/whoops": "~2.11", + "illuminate/collections": "^13", "illuminate/conditionable": "^13", "illuminate/contracts": "^13", "illuminate/macroable": "^13", diff --git a/src/Illuminate/Database/Eloquent/Collection.php b/src/Illuminate/Database/Eloquent/Collection.php index 67d7b6bf..0460b96c 100755 --- a/src/Illuminate/Database/Eloquent/Collection.php +++ b/src/Illuminate/Database/Eloquent/Collection.php @@ -82,7 +82,6 @@ public function contains($key, $operator = null, $value = null) * @param string $key * @return static */ - #[\Override] public function fetch($key) { return new static(array_fetch($this->toArray(), $key)); diff --git a/src/Illuminate/Database/Query/Builder.php b/src/Illuminate/Database/Query/Builder.php index 2bb7b8b3..974650cf 100755 --- a/src/Illuminate/Database/Query/Builder.php +++ b/src/Illuminate/Database/Query/Builder.php @@ -1363,14 +1363,14 @@ public function pluck($column, $key = null) // otherwise we can just give these values back without a specific key. $results = new Collection($this->get($columns)); - $values = $results->fetch($columns[0])->all(); + $values = $results->pluck($columns[0])->all(); // If a key was specified and we have results, we will go ahead and combine // the values with the keys of all of the records so that the values can // be accessed by the key of the rows instead of simply being numeric. if ( ! is_null($key) && count($results) > 0) { - $keys = $results->fetch($key)->all(); + $keys = $results->pluck($key)->all(); return array_combine($keys, $values); } diff --git a/src/Illuminate/Support/Arr.php b/src/Illuminate/Support/Arr.php deleted file mode 100755 index 1071c58a..00000000 --- a/src/Illuminate/Support/Arr.php +++ /dev/null @@ -1,982 +0,0 @@ - $items, - $items instanceof Enumerable => $items->all(), - $items instanceof Arrayable => $items->toArray(), - $items instanceof WeakMap => iterator_to_array($items, false), - $items instanceof Traversable => iterator_to_array($items), - $items instanceof Jsonable => json_decode($items->toJson(), true), - $items instanceof JsonSerializable => (array) $items->jsonSerialize(), - is_object($items) => (array) $items, - default => throw new InvalidArgumentException('Items cannot be represented by a scalar value.'), - }; - } - - /** - * Run an associative map over each of the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param array $array - * @param callable $callback - * @return array - */ - public static function mapWithKeys(array $array, callable $callback) - { - $result = []; - - foreach ($array as $key => $value) { - $assoc = $callback($value, $key); - - foreach ($assoc as $mapKey => $mapValue) { - $result[$mapKey] = $mapValue; - } - } - - return $result; - } - - /** - * Partition the array into two arrays using the given callback. - * - * @param array $array - * @param callable $callback - * @return array - */ - public static function partition($array, callable $callback) - { - $passed = []; - $failed = []; - - foreach ($array as $key => $item) { - if ($callback($item, $key)) { - $passed[$key] = $item; - } else { - $failed[$key] = $item; - } - } - - return [$passed, $failed]; - } - - /** - * Select an array of values from an array. - * - * @param array $array - * @param array|string $keys - * @return array - */ - public static function select($array, $keys) - { - $keys = static::wrap($keys); - - return static::map($array, function ($item) use ($keys) { - $result = []; - - foreach ($keys as $key) { - if (Arr::accessible($item) && Arr::exists($item, $key)) { - $result[$key] = $item[$key]; - } elseif (is_object($item) && isset($item->{$key})) { - $result[$key] = $item->{$key}; - } - } - - return $result; - }); - } - - /** - * Determine whether the given value is array accessible. - * - * @param mixed $value - * @return bool - */ - public static function accessible($value): bool - { - return is_array($value) || $value instanceof ArrayAccess; - } - - /** - * Add an element to an array using "dot" notation if it doesn't exist. - * - * @param array $array - * @param string $key - * @param mixed $value - * - * @return array - */ - public static function add(array $array, string $key, mixed $value): array - { - if (is_null(static::get($array, $key))) - { - static::set($array, $key, $value); - } - - return $array; - } - - /** - * Build a new array using a callback. - * - * @param array $array - * @param \Closure $callback - * - * @return array - */ - public static function build(array $array, Closure $callback): array - { - $results = array(); - - foreach ($array as $key => $value) - { - [$innerKey, $innerValue] = call_user_func($callback, $key, $value); - - $results[$innerKey] = $innerValue; - } - - return $results; - } - - /** - * Collapse an array of arrays into a single array. - * - * @param iterable $array - * - * @return array - */ - public static function collapse(iterable $array): array - { - $results = []; - - foreach ($array as $values) { - if ($values instanceof Collection) { - $values = $values->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): array - { - $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 $array): array - { - return array(array_keys($array), array_values($array)); - } - - /** - * Flatten a multi-dimensional associative array with dots. - * - * @param array $array - * @param string $prepend - * - * @return array - */ - public static function dot(array $array, string $prepend = ''): array - { - $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(iterable $array): 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 items. - * - * @param array $array - * @param array|string $keys - * - * @return array - */ - public static function except(array $array, array|string $keys): array - { - static::forget($array, $keys); - - return $array; - } - - /** - * Determine if the given key exists in the provided array. - * - * @param \ArrayAccess|array $array - * @param int|float|string $key - * - * @return bool - */ - public static function exists(ArrayAccess|array $array, $key): bool - { - if ($array instanceof ArrayAccess) { - return $array->offsetExists($key); - } - - if (is_float($key)) { - $key = (string) $key; - } - - return array_key_exists($key, $array); - } - - - /** - * Fetch a flattened array of a nested array element. - * - * @param array $array - * @param string $key - * @return array - */ - public static function fetch($array, $key) - { - foreach (explode('.', $key) as $segment) - { - $results = array(); - - foreach ($array as $value) - { - if (array_key_exists($segment, $value = (array) $value)) - { - $results[] = $value[$segment]; - } - } - - $array = array_values($results); - } - - return array_values($results); - } - - /** - * Return the first element in an array passing a given truth test. - * - * @param array $array - * @param \Closure|null $callback - * @param mixed|null $default - * - * @return mixed - */ - public static function first(array $array, ?Closure $callback = null, mixed $default = null): mixed - { - 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); - } - - /** - * 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 $array, string $glue, string $finalGlue = ''): string - { - 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): array - { - return Collection::make($array)->keyBy($keyBy)->all(); - } - - /** - * Return the last element in an array passing a given truth test. - * - * @param array $array - * @param \Closure|null $callback - * @param mixed $default - * - * @return mixed - */ - public static function last(array $array, ?Closure $callback = null, $default = null): mixed - { - 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 array $array - * @param int $depth - * - * @return array - */ - public static function flatten(array $array, $depth = INF): array - { - $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 $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|null $array - * @param string|null $key - * @param mixed $default - * - * @return mixed - */ - public static function get($array, $key = null, $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 exists in an array using "dot" notation. - * - * @param \ArrayAccess|array $array - * @param string|array $keys - * @return bool - */ - public static function has($array, $keys): bool - { - $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): bool - { - 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): bool - { - return ! array_is_list($array); - } - - /** - * 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 $array): bool - { - return array_is_list($array); - } - - /** - * 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): array - { - $keys = array_keys($array); - - try { - $items = array_map($callback, $array, $keys); - } catch (ArgumentCountError) { - $items = array_map($callback, $array); - } - - return array_combine($keys, $items); - } - - /** - * Get a subset of the items from the given array. - * - * @param array $array - * @param array|string $keys - * - * @return array - */ - public static function only(array $array, array|string $keys): array - { - return array_intersect_key($array, array_flip((array) $keys)); - } - - /** - * Pluck an array of values from an array. - * - * @param array $array - * @param array|string|null $value - * @param string|null $key - * - * @return array - */ - public static function pluck(array $array, $value = null, $key = null): array - { - $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): array - { - $value = is_string($value) ? explode('.', $value) : $value; - - $key = is_null($key) || is_array($key) ? $key : explode('.', $key); - - return [$value, $key]; - } - - /** - * 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 $array, $value, $key = null): array - { - if (func_num_args() == 2) { - array_unshift($array, $value); - } else { - $array = [$key => $value] + $array; - } - - return $array; - } - - /** - * Prepend the key names of an associative array. - * - * @param array $array - * @param string $prependWith - * @return array - */ - public static function prependKeysWith($array, $prependWith): array - { - return Collection::make($array)->mapWithKeys(function ($item, $key) use ($prependWith) { - return [$prependWith.$key => $item]; - })->all(); - } - - /** - * Get a value from the array, and remove it. - * - * @param array $array - * @param string $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): string - { - 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 $key - * @param mixed $value - * @return array - */ - public static function set(&$array, $key, $value): array - { - if (is_null($key)) return $array = $value; - - $keys = explode('.', $key); - - while (count($keys) > 1) - { - $key = array_shift($keys); - - // 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 =& $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): array - { - if (is_null($seed)) { - shuffle($array); - } else { - mt_srand($seed); - shuffle($array); - mt_srand(); - } - - return $array; - } - - /** - * Sort the array using the given Closure. - * - * @param array $array - * @param callable|array|string|null $callback - * - * @return array - */ - public static function sort(array $array, $callback = null): array - { - 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): array - { - foreach ($array as &$value) { - if (is_array($value)) { - $value = static::sortRecursive($value, $options, $descending); - } - } - - if (! array_is_list($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 $array): string - { - $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): array - { - 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, $key) { - 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): array - { - if (is_null($value)) { - return []; - } - - return is_array($value) ? $value : [$value]; - } -} diff --git a/src/Illuminate/Support/Collection.php b/src/Illuminate/Support/Collection.php deleted file mode 100755 index 01aec573..00000000 --- a/src/Illuminate/Support/Collection.php +++ /dev/null @@ -1,2050 +0,0 @@ - - * @implements \Illuminate\Support\Enumerable - */ -class Collection implements ArrayAccess, ArrayableInterface, CanBeEscapedWhenCastToString, Enumerable, JsonableInterface -{ - /** - * @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 - */ - public function __construct($items = []) - { - $this->items = $this->getArrayableItems($items); - } - - // ponytail: Laravel 4.2 holdovers kept so the fork's Database layer and existing - // callers keep working across the L13 shape change; removed in migration task 2.5 - // (lists → pluck, fetch → pluck) once app call-sites are converted. - - /** - * Get an array with the values of a given key. - * - * @param string $value - * @param string|null $key - * @return array - */ - public function lists(string $value, ?string $key = null): array - { - $results = []; - - foreach ($this->items as $item) { - $itemValue = is_object($item) ? $item->{$value} : $item[$value]; - - if (is_null($key)) { - $results[] = $itemValue; - } else { - $itemKey = is_object($item) ? $item->{$key} : $item[$key]; - - $results[$itemKey] = $itemValue; - } - } - - return $results; - } - - /** - * Fetch a nested element of the collection. - * - * @param string $key - * @return static - */ - public function fetch($key) - { - return new static(array_fetch($this->items, $key)); - } - - /** - * Create a new instance of the collection. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items - * @return static - */ - protected function newInstance($items = []) - { - return new static($items); - } - - /** - * Create a collection with the given range. - * - * @param int $from - * @param int $to - * @param int $step - * @return static - */ - public static function range($from, $to, $step = 1, ...$args) - { - return new static(range($from, $to, $step), ...$args); - } - - /** - * 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 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) - ->reject(fn ($item) => is_null($item)) - ->sort()->values(); - - $count = $values->count(); - - if ($count === 0) { - return; - } - - $middle = intdiv($count, 2); - - if ($count % 2) { - return $values->get($middle); - } - - return $this->newInstance([ - $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->isEmpty()) { - return; - } - - $collection = isset($key) ? $this->pluck($key) : $this; - - $counts = $this->newInstance(); - - $collection->each(fn ($value) => $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1); - - $sorted = $counts->sort(); - - $highestValue = $sorted->last(); - - return $sorted->filter(fn ($value) => $value == $highestValue) - ->sort()->keys()->all(); - } - - /** - * Collapse the collection of items into a single array. - * - * @return static - */ - public function collapse() - { - return $this->newInstance(Arr::collapse($this->items)); - } - - /** - * Collapse the collection of items into a single array while preserving its keys. - * - * @return static - */ - public function collapseWithKeys() - { - if (! $this->items) { - return $this->newInstance(); - } - - $results = []; - - foreach ($this->items as $key => $values) { - if ($values instanceof Collection) { - $values = $values->all(); - } elseif (! is_array($values)) { - continue; - } - - $results[$key] = $values; - } - - if (! $results) { - return $this->newInstance(); - } - - return $this->newInstance(array_replace(...$results)); - } - - /** - * 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)) { - return array_any($this->items, $key); - } - - return in_array($key, $this->items); - } - - return $this->contains($this->operatorForWhere(...func_get_args())); - } - - /** - * Determine if an item exists, using strict comparison. - * - * @param (callable(TValue): bool)|TValue|array-key $key - * @param TValue|null $value - * @return bool - */ - public function containsStrict($key, $value = null) - { - if (func_num_args() === 2) { - return $this->contains(fn ($item) => data_get($item, $key) === $value); - } - - if ($this->useAsCallable($key)) { - return ! is_null($this->first($key)); - } - - return in_array($key, $this->items, true); - } - - /** - * 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()); - } - - /** - * Determine if an item is not contained in the enumerable, using strict comparison. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function doesntContainStrict($key, $operator = null, $value = null) - { - return ! $this->containsStrict(...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 $this->newInstance(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 $this->newInstance(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 $this->newInstance(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 $this->newInstance(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 $this->newInstance(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 $this->newInstance(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 $this->newInstance(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Retrieve duplicate items from the collection. - * - * @template TMapValue - * - * @param (callable(TValue): TMapValue)|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 = $this->newInstance(); - - 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. - * - * @template TMapValue - * - * @param (callable(TValue): TMapValue)|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 fn ($a, $b) => $a === $b; - } - - return fn ($a, $b) => $a == $b; - } - - /** - * Get all items except for those with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array|string $keys - * @return static - */ - public function except($keys) - { - if (is_null($keys)) { - return $this->newInstance($this->items); - } - - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } elseif (! is_array($keys)) { - $keys = func_get_args(); - } - - return $this->newInstance(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 $this->newInstance(Arr::where($this->items, $callback)); - } - - return $this->newInstance(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 $this->newInstance(Arr::flatten($this->items, $depth)); - } - - /** - * Flip the items in the collection. - * - * @return static - */ - public function flip() - { - return $this->newInstance(array_flip($this->items)); - } - - /** - * Remove an item from the collection by key. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|TKey $keys - * @return $this - */ - public function forget($keys) - { - foreach ($this->getArrayableItems($keys) as $key) { - $this->offsetUnset($key); - } - - return $this; - } - - /** - * Get an item from the collection by key. - * - * @template TGetDefault - * - * @param TKey|null $key - * @param TGetDefault|(\Closure(): TGetDefault) $default - * @return TValue|TGetDefault - */ - public function get($key, $default = null) - { - $key ??= ''; - - 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. - * - * @template TGetOrPutValue - * - * @param mixed $key - * @param TGetOrPutValue|(\Closure(): TGetOrPutValue) $value - * @return TValue|TGetOrPutValue - */ - public function getOrPut($key, $value) - { - if (array_key_exists($key ?? '', $this->items)) { - return $this->items[$key ?? '']; - } - - $this->offsetSet($key, $value = value($value)); - - return $value; - } - - /** - * {@inheritDoc} - */ - #[\Override] - 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 \UnitEnum => enum_value($groupKey), - $groupKey instanceof \Stringable, is_null($groupKey) => (string) $groupKey, - default => $groupKey, - }; - - if (! array_key_exists($groupKey, $results)) { - $results[$groupKey] = $this->newInstance(); - } - - $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); - } - } - - $result = $this->newInstance($results); - - if (! empty($nextGroups)) { - return $result->map->groupBy($nextGroups, $preserveKeys); - } - - return $result; - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function keyBy($keyBy) - { - $keyBy = $this->valueRetriever($keyBy); - - $results = []; - - foreach ($this->items as $key => $item) { - $resolvedKey = $keyBy($item, $key); - - if ($resolvedKey instanceof \UnitEnum) { - $resolvedKey = enum_value($resolvedKey); - } - - if (is_object($resolvedKey)) { - $resolvedKey = (string) $resolvedKey; - } - - if (is_null($resolvedKey)) { - $resolvedKey = (string) $resolvedKey; - } - - $results[$resolvedKey] = $item; - } - - return $this->newInstance($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(); - - return array_all($keys, fn ($key) => array_key_exists($key ?? '', $this->items)); - } - - /** - * Determine if any of the keys exist in the collection. - * - * @param TKey|array $key - * @return bool - */ - public function hasAny($key) - { - if ($this->isEmpty()) { - return false; - } - - $keys = is_array($key) ? $key : func_get_args(); - - return array_any($keys, fn ($key) => array_key_exists($key ?? '', $this->items)); - } - - /** - * Concatenate values of a given key as a string. - * - * @param (callable(TValue, TKey): mixed)|string|null $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 $this->newInstance(array_intersect($this->items, $this->getArrayableItems($items))); - } - - /** - * Intersect the collection with the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function intersectUsing($items, callable $callback) - { - return $this->newInstance(array_uintersect($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Intersect the collection with the given items with additional index check. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersectAssoc($items) - { - return $this->newInstance(array_intersect_assoc($this->items, $this->getArrayableItems($items))); - } - - /** - * Intersect the collection with the given items with additional index check, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function intersectAssocUsing($items, callable $callback) - { - return $this->newInstance(array_intersect_uassoc($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Intersect the collection with the given items by key. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersectByKeys($items) - { - return $this->newInstance(array_intersect_key( - $this->items, $this->getArrayableItems($items) - )); - } - - /** - * Determine if the collection is empty or not. - * - * @phpstan-assert-if-true null $this->first() - * @phpstan-assert-if-true null $this->last() - * - * @phpstan-assert-if-false TValue $this->first() - * @phpstan-assert-if-false TValue $this->last() - * - * @return bool - */ - public function isEmpty() - { - return empty($this->items); - } - - /** - * Determine if the collection contains exactly one item. If a callback is provided, determine if exactly one item matches the condition. - * - * @param (callable(TValue, TKey): bool)|null $callback - * @return bool - * - * @deprecated 12.49.0 Use the `hasSole()` method instead. - */ - public function containsOneItem(?callable $callback = null): bool - { - return $this->hasSole($callback); - } - - /** - * Determine if the collection contains multiple items. - * - * @param (callable(TValue, TKey): bool)|null $callback - * @return bool - * - * @deprecated 12.50.0 Use the `hasMany()` method instead. - */ - public function containsManyItems(?callable $callback = null): bool - { - return $this->hasMany($callback); - } - - /** - * 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 TValue|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 = $this->newInstance($this->items); - - $finalItem = $collection->pop(); - - return $collection->implode($glue).$finalGlue.$finalItem; - } - - /** - * Get the keys of the collection items. - * - * @return static - */ - public function keys() - { - return $this->newInstance(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 \Closure|string|int|array|null $value - * @param \Closure|string|null $key - * @return static - */ - public function pluck($value, $key = null) - { - return $this->newInstance(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 $this->newInstance(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 $this->newInstance($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) - { - return $this->newInstance(Arr::mapWithKeys($this->items, $callback)); - } - - /** - * Merge the collection with the given items. - * - * @template TMergeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function merge($items) - { - return $this->newInstance(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 $this->newInstance(array_merge_recursive($this->items, $this->getArrayableItems($items))); - } - - /** - * Multiply the items in the collection by the multiplier. - * - * @param int $multiplier - * @return static - */ - public function multiply(int $multiplier) - { - $new = $this->newInstance(); - - for ($i = 0; $i < $multiplier; $i++) { - $new->push(...$this->items); - } - - return $new; - } - - /** - * 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 $this->newInstance(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 $this->newInstance($this->items + $this->getArrayableItems($items)); - } - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return ($step is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function nth($step, $offset = 0) - { - if ($step < 1) { - throw new InvalidArgumentException('Step value must be at least 1.'); - } - - $new = []; - - $position = 0; - - foreach ($this->slice($offset)->items as $item) { - if ($position % $step === 0) { - $new[] = $item; - } - - $position++; - } - - return $this->newInstance($new); - } - - /** - * Get the items with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array|string|null $keys - * @return static - */ - public function only($keys) - { - if (is_null($keys)) { - return $this->newInstance($this->items); - } - - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } - - $keys = is_array($keys) ? $keys : func_get_args(); - - return $this->newInstance(Arr::only($this->items, $keys)); - } - - /** - * Select specific values from the items within the collection. - * - * @param \Illuminate\Support\Enumerable|list|string|null $keys - * @return static - */ - public function select($keys) - { - if (is_null($keys)) { - return $this->newInstance($this->items); - } - - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } - - $keys = is_array($keys) ? $keys : func_get_args(); - - return $this->newInstance(Arr::select($this->items, $keys)); - } - - /** - * Get and remove the last N items from the collection. - * - * @param int $count - * @return ($count is 1 ? TValue|null : static) - */ - public function pop($count = 1) - { - if ($count < 1) { - return $this->newInstance(); - } - - if ($count === 1) { - return array_pop($this->items); - } - - if ($this->isEmpty()) { - return $this->newInstance(); - } - - $results = []; - - $collectionCount = $this->count(); - - foreach (range(1, min($count, $collectionCount)) as $item) { - $results[] = array_pop($this->items); - } - - return $this->newInstance($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_num_args() > 1 ? func_get_args() : [$value])); - - 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; - } - - /** - * Prepend one or more items to the beginning of the collection. - * - * @param TValue ...$values - * @return $this - */ - public function unshift(...$values) - { - array_unshift($this->items, ...$values); - - return $this; - } - - /** - * Push all of the given items onto the collection. - * - * @template TConcatKey of array-key - * @template TConcatValue - * - * @param iterable $source - * @return static - */ - public function concat($source) - { - $result = $this->newInstance($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(self): int)|int|null $number - * @param bool $preserveKeys - * @return ($number is null ? TValue : static) - * - * @throws \InvalidArgumentException - */ - public function random($number = null, $preserveKeys = false) - { - if (is_null($number)) { - return Arr::random($this->items); - } - - if (is_callable($number)) { - return $this->newInstance(Arr::random($this->items, $number($this), $preserveKeys)); - } - - return $this->newInstance(Arr::random($this->items, $number, $preserveKeys)); - } - - /** - * Replace the collection items with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function replace($items) - { - return $this->newInstance(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 $this->newInstance(array_replace_recursive($this->items, $this->getArrayableItems($items))); - } - - /** - * Reverse items order. - * - * @return static - */ - public function reverse() - { - return $this->newInstance(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|false - */ - public function search($value, $strict = false) - { - if (! $this->useAsCallable($value)) { - return array_search($value, $this->items, $strict); - } - - return array_find_key($this->items, $value) ?? false; - } - - /** - * Get the item before the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function before($value, $strict = false) - { - $key = $this->search($value, $strict); - - if ($key === false) { - return null; - } - - $position = ($keys = $this->keys())->search($key); - - if ($position === 0) { - return null; - } - - return $this->get($keys->get($position - 1)); - } - - /** - * Get the item after the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function after($value, $strict = false) - { - $key = $this->search($value, $strict); - - if ($key === false) { - return null; - } - - $position = ($keys = $this->keys())->search($key); - - if ($position === $keys->count() - 1) { - return null; - } - - return $this->get($keys->get($position + 1)); - } - - /** - * Get and remove the first N items from the collection. - * - * @param int<0, max> $count - * @return ($count is 1 ? TValue|null : static) - * - * @throws \InvalidArgumentException - */ - public function shift($count = 1) - { - if ($count < 0) { - throw new InvalidArgumentException('Number of shifted items may not be less than zero.'); - } - - if ($this->isEmpty()) { - return null; - } - - if ($count === 0) { - return $this->newInstance(); - } - - if ($count === 1) { - return array_shift($this->items); - } - - $results = []; - - $collectionCount = $this->count(); - - foreach (range(1, min($count, $collectionCount)) as $item) { - $results[] = array_shift($this->items); - } - - return $this->newInstance($results); - } - - /** - * Shuffle the items in the collection. - * - * @return static - */ - public function shuffle() - { - return $this->newInstance(Arr::shuffle($this->items)); - } - - /** - * Create chunks representing a "sliding window" view of the items in the collection. - * - * @param positive-int $size - * @param positive-int $step - * @return static - * - * @throws \InvalidArgumentException - */ - public function sliding($size = 2, $step = 1) - { - if ($size < 1) { - throw new InvalidArgumentException('Size value must be at least 1.'); - } elseif ($step < 1) { - throw new InvalidArgumentException('Step value must be at least 1.'); - } - - $chunks = floor(($this->count() - $size) / $step) + 1; - - return static::times($chunks, fn ($number) => $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 $this->newInstance($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 $this->newInstance($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 $this->newInstance(array_slice($this->items, $offset, $length, true)); - } - - /** - * Split a collection into a certain number of groups. - * - * @param int $numberOfGroups - * @return ($numberOfGroups is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function split($numberOfGroups) - { - if ($numberOfGroups < 1) { - throw new InvalidArgumentException('Number of groups must be at least 1.'); - } - - if ($this->isEmpty()) { - return $this->newInstance(); - } - - $groups = $this->newInstance(); - - $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($this->newInstance(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 ($numberOfGroups is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function splitIn($numberOfGroups) - { - if ($numberOfGroups < 1) { - throw new InvalidArgumentException('Number of groups must be at least 1.'); - } - - return $this->chunk((int) 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|null $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(); - } - - /** - * Determine if the collection contains a single item, optionally matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasSole($key = null, $operator = null, $value = null): bool - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - return $this - ->unless($filter == null) - ->filter($filter) - ->count() === 1; - } - - /** - * 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 - * @param bool $preserveKeys - * @return ($preserveKeys is true ? static : static>) - */ - public function chunk($size, $preserveKeys = true) - { - if ($size <= 0) { - return $this->newInstance(); - } - - $chunks = []; - - foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { - $chunks[] = $this->newInstance($chunk); - } - - return $this->newInstance($chunks); - } - - /** - * Chunk the collection into chunks with a callback. - * - * @param callable(TValue, TKey, static): bool $callback - * @return static> - */ - public function chunkWhile(callable $callback) - { - return $this->newInstance( - $this->lazy()->chunkWhile($callback)->mapInto(static::class) - ); - } - - /** - * Sort through each item with a callback. - * - * @param (callable(TValue, TValue): int)|null|int $callback - * @return static - */ - public function sort($callback = null) - { - $items = $this->items; - - $callback && is_callable($callback) - ? uasort($items, $callback) - : asort($items, $callback ?? SORT_REGULAR); - - return $this->newInstance($items); - } - - /** - * Sort items in descending order. - * - * @param int-mask-of $options - * @return static - */ - public function sortDesc($options = SORT_REGULAR) - { - $items = $this->items; - - arsort($items, $options); - - return $this->newInstance($items); - } - - /** - * Sort the collection using the given callback. - * - * @param array|(callable(TValue, TKey): mixed)|string|int $callback - * @param int-mask-of $options - * @param SortDirection|bool $descending - * @return static - */ - public function sortBy($callback, $options = SORT_REGULAR, $descending = false) - { - if (is_array($callback) && ! is_callable($callback)) { - return $this->sortByMany($callback, $options); - } - - $results = []; - - $callback = $this->valueRetriever($callback); - - // First we will loop through the items and get the comparator from a callback - // function which we were given. Then, we will sort the returned values and - // grab all the corresponding values for the sorted keys from this array. - foreach ($this->items as $key => $value) { - $results[$key] = $callback($value, $key); - } - - match ($descending) { - false, SortDirection::Ascending => asort($results, $options), - true, SortDirection::Descending => arsort($results, $options), - }; - - // Once we have sorted all of the keys in the array, we will loop through them - // and grab the corresponding model so we can set the underlying items list - // to the sorted version. Then we'll just return the collection instance. - foreach (array_keys($results) as $key) { - $results[$key] = $this->items[$key]; - } - - return $this->newInstance($results); - } - - /** - * Sort the collection using multiple comparisons. - * - * @param array $comparisons - * @param int-mask-of $options - * @return static - */ - protected function sortByMany(array $comparisons = [], int $options = SORT_REGULAR) - { - $items = $this->items; - - uasort($items, function ($a, $b) use ($comparisons, $options) { - foreach ($comparisons as $comparison) { - $comparison = Arr::wrap($comparison); - - $prop = $comparison[0]; - - $direction = match (Arr::get($comparison, 1, true)) { - true, 'asc', SortDirection::Ascending => SortDirection::Ascending, - false, 'desc', SortDirection::Descending => SortDirection::Descending, - default => SortDirection::Descending, // for backwards compatibility - }; - - if (! is_string($prop) && is_callable($prop)) { - $result = $prop($a, $b); - } else { - $values = [data_get($a, $prop), data_get($b, $prop)]; - - if ($direction === SortDirection::Descending) { - $values = array_reverse($values); - } - - if (($options & SORT_FLAG_CASE) === SORT_FLAG_CASE) { - if (($options & SORT_NATURAL) === SORT_NATURAL) { - $result = strnatcasecmp($values[0], $values[1]); - } else { - $result = strcasecmp($values[0], $values[1]); - } - } else { - $result = match ($options) { - SORT_NUMERIC => (int) $values[0] <=> (int) $values[1], - SORT_STRING => strcmp($values[0], $values[1]), - SORT_NATURAL => strnatcmp((string) $values[0], (string) $values[1]), - SORT_LOCALE_STRING => strcoll($values[0], $values[1]), - default => $values[0] <=> $values[1], - }; - } - } - - if ($result === 0) { - continue; - } - - return $result; - } - }); - - return $this->newInstance($items); - } - - /** - * Sort the collection in descending order using the given callback. - * - * @param array|(callable(TValue, TKey): mixed)|string|int $callback - * @param int-mask-of $options - * @return static - */ - public function sortByDesc($callback, $options = SORT_REGULAR) - { - if (is_array($callback) && ! is_callable($callback)) { - foreach ($callback as $index => $key) { - $comparison = Arr::wrap($key); - - $comparison[1] = SortDirection::Descending; - - $callback[$index] = $comparison; - } - } - - return $this->sortBy($callback, $options, true); - } - - /** - * Sort the collection keys. - * - * @param int-mask-of $options - * @param SortDirection|bool $descending - * @return static - */ - public function sortKeys($options = SORT_REGULAR, $descending = false) - { - $items = $this->items; - - match ($descending) { - false, SortDirection::Ascending => ksort($items, $options), - true, SortDirection::Descending => krsort($items, $options), - }; - - return $this->newInstance($items); - } - - /** - * Sort the collection keys in descending order. - * - * @param int-mask-of $options - * @return static - */ - public function sortKeysDesc($options = SORT_REGULAR) - { - return $this->sortKeys($options, SortDirection::Descending); - } - - /** - * Sort the collection keys using a callback. - * - * @param callable(TKey, TKey): int $callback - * @return static - */ - public function sortKeysUsing(callable $callback) - { - $items = $this->items; - - uksort($items, $callback); - - return $this->newInstance($items); - } - - /** - * Splice a portion of the underlying collection array. - * - * @param int $offset - * @param int|null $length - * @param array $replacement - * @return static - */ - public function splice($offset, $length = null, $replacement = []) - { - if (func_num_args() === 1) { - return $this->newInstance(array_splice($this->items, $offset)); - } - - return $this->newInstance(array_splice($this->items, $offset, $length, $this->getArrayableItems($replacement))); - } - - /** - * Take the first or last {$limit} items. - * - * @param int $limit - * @return static - */ - public function take($limit) - { - if ($limit < 0) { - return $this->slice($limit, abs($limit)); - } - - return $this->slice(0, $limit); - } - - /** - * Take items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeUntil($value) - { - return $this->newInstance($this->lazy()->takeUntil($value)->all()); - } - - /** - * Take items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeWhile($value) - { - return $this->newInstance($this->lazy()->takeWhile($value)->all()); - } - - /** - * Transform each item in the collection using a callback. - * - * @template TMapValue - * - * @param callable(TValue, TKey): TMapValue $callback - * @return $this - * - * @phpstan-this-out static - */ - public function transform(callable $callback) - { - $this->items = $this->map($callback)->all(); - - return $this; - } - - /** - * Flatten a multi-dimensional associative array with dots. - * - * @param int $depth - * @return static - */ - public function dot($depth = INF) - { - return $this->newInstance(Arr::dot($this->all(), '', $depth)); - } - - /** - * Convert a flatten "dot" notation array into an expanded array. - * - * @return static - */ - public function undot() - { - return $this->newInstance(Arr::undot($this->all())); - } - - /** - * Return only unique items from the collection array. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @param bool $strict - * @return static - */ - public function unique($key = null, $strict = false) - { - if (is_null($key) && $strict === false) { - return $this->newInstance(array_unique($this->items, SORT_REGULAR)); - } - - $callback = $this->valueRetriever($key); - - $exists = []; - - return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) { - if (in_array($id = $callback($item, $key), $exists, $strict)) { - return true; - } - - $exists[] = $id; - }); - } - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values() - { - return $this->newInstance(array_values($this->items)); - } - - /** - * Zip the collection together with one or more arrays. - * - * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); - * => [[1, 4], [2, 5], [3, 6]] - * - * @template TZipValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items - * @return static> - */ - public function zip($items) - { - $arrayableItems = array_map(fn ($items) => $this->getArrayableItems($items), func_get_args()); - - $params = array_merge([fn () => $this->newInstance(func_get_args()), $this->items], $arrayableItems); - - return $this->newInstance(array_map(...$params)); - } - - /** - * Pad collection to the specified length with a value. - * - * @template TPadValue - * - * @param int $size - * @param TPadValue $value - * @return static - */ - public function pad($size, $value) - { - return $this->newInstance(array_pad($this->items, $size, $value)); - } - - /** - * Get an iterator for the items. - * - * @return \ArrayIterator - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } - - /** - * Count the number of items in the collection. - * - * @return int<0, max> - */ - public function count(): int - { - return count($this->items); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function countBy($countBy = null) - { - return $this->newInstance($this->lazy()->countBy($countBy)->all()); - } - - /** - * Add an item to the collection. - * - * @param TValue $item - * @return $this - */ - public function add($item) - { - $this->items[] = $item; - - return $this; - } - - /** - * Get a base Support collection instance from this collection. - * - * @return \Illuminate\Support\Collection - */ - public function toBase() - { - return new self($this); - } - - /** - * Determine if an item exists at an offset. - * - * @param TKey $offset - * @return bool - */ - public function offsetExists($offset): bool - { - return isset($this->items[$offset]); - } - - /** - * Get an item at a given offset. - * - * @param TKey $offset - * @return TValue - */ - public function offsetGet($offset): mixed - { - return $this->items[$offset]; - } - - /** - * Set the item at a given offset. - * - * @param TKey|null $offset - * @param TValue $value - * @return void - */ - public function offsetSet($offset, $value): void - { - if (is_null($offset)) { - $this->items[] = $value; - } else { - $this->items[$offset] = $value; - } - } - - /** - * Unset the item at a given offset. - * - * @param TKey $offset - * @return void - */ - public function offsetUnset($offset): void - { - unset($this->items[$offset]); - } -} diff --git a/src/Illuminate/Support/Enumerable.php b/src/Illuminate/Support/Enumerable.php deleted file mode 100644 index 36e04705..00000000 --- a/src/Illuminate/Support/Enumerable.php +++ /dev/null @@ -1,1379 +0,0 @@ - - * @extends \IteratorAggregate - */ -interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable -{ - /** - * Create a new collection instance if the value isn't one already. - * - * @template TMakeKey of array-key - * @template TMakeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items - * @return static - */ - public static function make($items = []); - - /** - * Create a new instance by invoking the callback a given amount of times. - * - * @template TTimesValue - * - * @param int $number - * @param (callable(int): TTimesValue)|null $callback - * @return static - */ - public static function times($number, ?callable $callback = null); - - /** - * Create a collection with the given range. - * - * @param int $from - * @param int $to - * @param int $step - * @return static - */ - public static function range($from, $to, $step = 1); - - /** - * Wrap the given value in a collection if applicable. - * - * @template TWrapValue - * - * @param iterable|TWrapValue $value - * @return static - */ - public static function wrap($value); - - /** - * Get the underlying items from the given collection if applicable. - * - * @template TUnwrapKey of array-key - * @template TUnwrapValue - * - * @param array|static $value - * @return array - */ - public static function unwrap($value); - - /** - * Create a new instance with no items. - * - * @return static - */ - public static function empty(); - - /** - * Get all items in the enumerable. - * - * @return array - */ - public function all(); - - /** - * Alias for the "avg" method. - * - * @param (callable(TValue): float|int)|string|null $callback - * @return float|int|null - */ - public function average($callback = null); - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return float|int|null - */ - public function median($key = null); - - /** - * Get the mode of a given key. - * - * @param string|array|null $key - * @return array|null - */ - public function mode($key = null); - - /** - * Collapse the items into a single enumerable. - * - * @return static - */ - public function collapse(); - - /** - * Alias for the "contains" method. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function some($key, $operator = null, $value = null); - - /** - * Determine if an item exists, using strict comparison. - * - * @param (callable(TValue): bool)|TValue|array-key $key - * @param TValue|null $value - * @return bool - */ - public function containsStrict($key, $value = null); - - /** - * 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); - - /** - * Determine if an item exists in the enumerable. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function contains($key, $operator = null, $value = null); - - /** - * 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); - - /** - * 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); - - /** - * Dump the collection and end the script. - * - * @param mixed ...$args - * @return never - */ - public function dd(...$args); - - /** - * Dump the collection. - * - * @param mixed ...$args - * @return $this - */ - public function dump(...$args); - - /** - * Get the items that are not present in the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function diff($items); - - /** - * Get the items 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); - - /** - * Get the items whose keys and values are not present in the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function diffAssoc($items); - - /** - * Get the items 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); - - /** - * Get the items whose keys are not present in the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function diffKeys($items); - - /** - * Get the items 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); - - /** - * Retrieve duplicate items. - * - * @param (callable(TValue): bool)|string|null $callback - * @param bool $strict - * @return static - */ - public function duplicates($callback = null, $strict = false); - - /** - * Retrieve duplicate items using strict comparison. - * - * @param (callable(TValue): bool)|string|null $callback - * @return static - */ - public function duplicatesStrict($callback = null); - - /** - * Execute a callback over each item. - * - * @param callable(TValue, TKey): mixed $callback - * @return $this - */ - public function each(callable $callback); - - /** - * Execute a callback over each nested chunk of items. - * - * @param callable $callback - * @return static - */ - public function eachSpread(callable $callback); - - /** - * Determine if all items pass the given truth test. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function every($key, $operator = null, $value = null); - - /** - * Get all items except for those with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array $keys - * @return static - */ - public function except($keys); - - /** - * Run a filter over each of the items. - * - * @param (callable(TValue): bool)|null $callback - * @return static - */ - public function filter(?callable $callback = null); - - /** - * Apply the callback if the given "value" is (or resolves to) truthy. - * - * @template TWhenReturnType as null - * - * @param bool $value - * @param (callable($this): TWhenReturnType)|null $callback - * @param (callable($this): TWhenReturnType)|null $default - * @return $this|TWhenReturnType - */ - public function when($value, ?callable $callback = null, ?callable $default = null); - - /** - * Apply the callback if the collection is empty. - * - * @template TWhenEmptyReturnType - * - * @param (callable($this): TWhenEmptyReturnType) $callback - * @param (callable($this): TWhenEmptyReturnType)|null $default - * @return $this|TWhenEmptyReturnType - */ - public function whenEmpty(callable $callback, ?callable $default = null); - - /** - * Apply the callback if the collection is not empty. - * - * @template TWhenNotEmptyReturnType - * - * @param callable($this): TWhenNotEmptyReturnType $callback - * @param (callable($this): TWhenNotEmptyReturnType)|null $default - * @return $this|TWhenNotEmptyReturnType - */ - public function whenNotEmpty(callable $callback, ?callable $default = null); - - /** - * Apply the callback if the given "value" is (or resolves to) falsy. - * - * @template TUnlessReturnType - * - * @param bool $value - * @param (callable($this): TUnlessReturnType) $callback - * @param (callable($this): TUnlessReturnType)|null $default - * @return $this|TUnlessReturnType - */ - public function unless($value, callable $callback, ?callable $default = null); - - /** - * Apply the callback unless the collection is empty. - * - * @template TUnlessEmptyReturnType - * - * @param callable($this): TUnlessEmptyReturnType $callback - * @param (callable($this): TUnlessEmptyReturnType)|null $default - * @return $this|TUnlessEmptyReturnType - */ - public function unlessEmpty(callable $callback, ?callable $default = null); - - /** - * Apply the callback unless the collection is not empty. - * - * @template TUnlessNotEmptyReturnType - * - * @param callable($this): TUnlessNotEmptyReturnType $callback - * @param (callable($this): TUnlessNotEmptyReturnType)|null $default - * @return $this|TUnlessNotEmptyReturnType - */ - public function unlessNotEmpty(callable $callback, ?callable $default = null); - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param mixed $operator - * @param mixed $value - * @return static - */ - public function where($key, $operator = null, $value = null); - - /** - * Filter items where the value for the given key is null. - * - * @param string|null $key - * @return static - */ - public function whereNull($key = null); - - /** - * Filter items where the value for the given key is not null. - * - * @param string|null $key - * @return static - */ - public function whereNotNull($key = null); - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param mixed $value - * @return static - */ - public function whereStrict($key, $value); - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @param bool $strict - * @return static - */ - public function whereIn($key, $values, $strict = false); - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereInStrict($key, $values); - - /** - * Filter items such that the value of the given key is between the given values. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereBetween($key, $values); - - /** - * Filter items such that the value of the given key is not between the given values. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereNotBetween($key, $values); - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @param bool $strict - * @return static - */ - public function whereNotIn($key, $values, $strict = false); - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereNotInStrict($key, $values); - - /** - * Filter the items, removing any items that don't match the given type(s). - * - * @template TWhereInstanceOf - * - * @param class-string|array> $type - * @return static - */ - public function whereInstanceOf($type); - - /** - * Get the first item from the enumerable 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); - - /** - * Get the first item by the given key value pair. - * - * @param string $key - * @param mixed $operator - * @param mixed $value - * @return TValue|null - */ - public function firstWhere($key, $operator = null, $value = null); - - /** - * Get a flattened array of the items in the collection. - * - * @param int $depth - * @return static - */ - public function flatten($depth = INF); - - /** - * Flip the values with their keys. - * - * @return static - */ - public function flip(); - - /** - * 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); - - /** - * Group an associative array by a field or using a callback. - * - * @template TGroupKey of array-key|\UnitEnum|\Stringable - * - * @param (callable(TValue, TKey): TGroupKey)|array|string $groupBy - * @param bool $preserveKeys - * @return static< - * ($groupBy is (array|string) - * ? array-key - * : (TGroupKey is \UnitEnum ? array-key : (TGroupKey is \Stringable ? string : TGroupKey))), - * static<($preserveKeys is true ? TKey : int), ($groupBy is array ? mixed : TValue)> - * > - */ - public function groupBy($groupBy, $preserveKeys = false); - - /** - * Key an associative array by a field or using a callback. - * - * @template TNewKey of array-key|\UnitEnum - * - * @param (callable(TValue, TKey): TNewKey)|array|string $keyBy - * @return static<($keyBy is (array|string) ? array-key : (TNewKey is \UnitEnum ? array-key : TNewKey)), TValue> - */ - public function keyBy($keyBy); - - /** - * Determine if an item exists in the collection by key. - * - * @param TKey|array $key - * @return bool - */ - public function has($key); - - /** - * Determine if any of the keys exist in the collection. - * - * @param mixed $key - * @return bool - */ - public function hasAny($key); - - /** - * Concatenate values of a given key as a string. - * - * @param (callable(TValue, TKey): mixed)|string $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null); - - /** - * Intersect the collection with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersect($items); - - /** - * Intersect the collection with the given items, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function intersectUsing($items, callable $callback); - - /** - * Intersect the collection with the given items with additional index check. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersectAssoc($items); - - /** - * Intersect the collection with the given items with additional index check, using the callback. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @param callable(TValue, TValue): int $callback - * @return static - */ - public function intersectAssocUsing($items, callable $callback); - - /** - * Intersect the collection with the given items by key. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function intersectByKeys($items); - - /** - * Determine if the collection is empty or not. - * - * @return bool - */ - public function isEmpty(); - - /** - * Determine if the collection is not empty. - * - * @return bool - */ - public function isNotEmpty(); - - /** - * Determine if the collection contains a single item. - * - * @return bool - */ - public function containsOneItem(); - - /** - * Determine if the collection contains multiple items. - * - * @return bool - */ - public function containsManyItems(); - - /** - * Determine if the collection contains a single item, optionally matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasSole($key = null, $operator = null, $value = null); - - /** - * Determine if the collection contains multiple items, optionally matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasMany($key = null, $operator = null, $value = null); - - /** - * 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 = ''); - - /** - * Get the keys of the collection items. - * - * @return static - */ - public function keys(); - - /** - * 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); - - /** - * Run a map over each of the items. - * - * @template TMapValue - * - * @param callable(TValue, TKey): TMapValue $callback - * @return static - */ - public function map(callable $callback); - - /** - * Run a map over each nested chunk of items. - * - * @param callable $callback - * @return static - */ - public function mapSpread(callable $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); - - /** - * Run a grouping map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @template TMapToGroupsKey of array-key - * @template TMapToGroupsValue - * - * @param callable(TValue, TKey): array $callback - * @return static> - */ - public function mapToGroups(callable $callback); - - /** - * 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); - - /** - * Map a collection and flatten the result by a single level. - * - * @template TFlatMapKey of array-key - * @template TFlatMapValue - * - * @param callable(TValue, TKey): (\Illuminate\Support\Collection|array) $callback - * @return static - */ - public function flatMap(callable $callback); - - /** - * Map the values into a new class. - * - * @template TMapIntoValue - * - * @param class-string $class - * @return static - */ - public function mapInto($class); - - /** - * Merge the collection with the given items. - * - * @template TMergeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function merge($items); - - /** - * Recursively merge the collection with the given items. - * - * @template TMergeRecursiveValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function mergeRecursive($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); - - /** - * Union the collection with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function union($items); - - /** - * Get the min value of a given key. - * - * @template TMinResult = mixed - * - * @param (callable(TValue): TMinResult)|string|null $callback - * @return ($callback is callable ? ?TMinResult : ($callback is null ? ?TValue : mixed)) - */ - public function min($callback = null); - - /** - * Get the max value of a given key. - * - * @template TMaxResult = mixed - * - * @param (callable(TValue): TMaxResult)|string|null $callback - * @return ($callback is callable ? ?TMaxResult : ($callback is null ? ?TValue : mixed)) - */ - public function max($callback = null); - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return static - */ - public function nth($step, $offset = 0); - - /** - * Get the items with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array|string $keys - * @return static - */ - public function only($keys); - - /** - * "Paginate" the collection by slicing it into a smaller collection. - * - * @param int $page - * @param int $perPage - * @return static - */ - public function forPage($page, $perPage); - - /** - * Partition the collection into two arrays using the given callback or key. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return static, static> - */ - public function partition($key, $operator = null, $value = null); - - /** - * Push all of the given items onto the collection. - * - * @template TConcatKey of array-key - * @template TConcatValue - * - * @param iterable $source - * @return static - */ - public function concat($source); - - /** - * Get one or a specified number of items randomly from the collection. - * - * @param int|null $number - * @return ($number is null ? TValue : static) - * - * @throws \InvalidArgumentException - */ - public function random($number = null); - - /** - * Reduce the collection to a single value. - * - * @template TReduceInitial - * @template TReduceReturnType - * - * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback - * @param TReduceInitial $initial - * @return TReduceInitial|TReduceReturnType - */ - public function reduce(callable $callback, $initial = null); - - /** - * Reduce the collection to a single value by mutating an initial value. - * - * @template TReduceIntoInitial - * - * @param TReduceIntoInitial $initial - * @param callable(TReduceIntoInitial, TValue, TKey): void $callback - * @return TReduceIntoInitial - */ - public function reduceInto($initial, callable $callback); - - /** - * Reduce the collection to multiple aggregate values. - * - * @param callable $callback - * @param mixed ...$initial - * @return array - * - * @throws \UnexpectedValueException - */ - public function reduceSpread(callable $callback, ...$initial); - - /** - * Replace the collection items with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function replace($items); - - /** - * Recursively replace the collection items with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function replaceRecursive($items); - - /** - * Reverse items order. - * - * @return static - */ - public function reverse(); - - /** - * 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|false - */ - public function search($value, $strict = false); - - /** - * Get the item before the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function before($value, $strict = false); - - /** - * Get the item after the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function after($value, $strict = false); - - /** - * Shuffle the items in the collection. - * - * @return static - */ - public function shuffle(); - - /** - * 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); - - /** - * Skip the first {$count} items. - * - * @param int $count - * @return static - */ - public function skip($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); - - /** - * Skip items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function skipWhile($value); - - /** - * Get a slice of items from the enumerable. - * - * @param int $offset - * @param int|null $length - * @return static - */ - public function slice($offset, $length = null); - - /** - * Split a collection into a certain number of groups. - * - * @param int $numberOfGroups - * @return static - */ - public function split($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); - - /** - * Get the first item in the collection but throw an exception if no matching items exist. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return TValue - * - * @throws \Illuminate\Support\ItemNotFoundException - */ - public function firstOrFail($key = null, $operator = null, $value = null); - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @return static - */ - public function chunk($size); - - /** - * Chunk the collection into chunks with a callback. - * - * @param callable(TValue, TKey, static): bool $callback - * @return static> - */ - public function chunkWhile(callable $callback); - - /** - * Chunk the collection into chunks by comparing adjacent values using the given key or callback. - * - * @param (callable(TValue, TKey): mixed)|string $key - * @return static> - */ - public function chunkBy($key); - - /** - * Split a collection into a certain number of groups, and fill the first groups completely. - * - * @param int $numberOfGroups - * @return static - */ - public function splitIn($numberOfGroups); - - /** - * Sort through each item with a callback. - * - * @param (callable(TValue, TValue): int)|null|int $callback - * @return static - */ - public function sort($callback = null); - - /** - * Sort items in descending order. - * - * @param int-mask-of $options - * @return static - */ - public function sortDesc($options = SORT_REGULAR); - - /** - * Sort the collection using the given callback. - * - * @param array|(callable(TValue, TKey): mixed)|string|int $callback - * @param int-mask-of $options - * @param bool $descending - * @return static - */ - public function sortBy($callback, $options = SORT_REGULAR, $descending = false); - - /** - * Sort the collection in descending order using the given callback. - * - * @param array|(callable(TValue, TKey): mixed)|string|int $callback - * @param int-mask-of $options - * @return static - */ - public function sortByDesc($callback, $options = SORT_REGULAR); - - /** - * Sort the collection keys. - * - * @param int-mask-of $options - * @param bool $descending - * @return static - */ - public function sortKeys($options = SORT_REGULAR, $descending = false); - - /** - * Sort the collection keys in descending order. - * - * @param int-mask-of $options - * @return static - */ - public function sortKeysDesc($options = SORT_REGULAR); - - /** - * Sort the collection keys using a callback. - * - * @param callable(TKey, TKey): int $callback - * @return static - */ - public function sortKeysUsing(callable $callback); - - /** - * Get the sum of the given values. - * - * @param (callable(TValue, TKey): mixed)|string|null $callback - * @return mixed - */ - public function sum($callback = null); - - /** - * Take the first or last {$limit} items. - * - * @param int $limit - * @return static - */ - public function take($limit); - - /** - * Take items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeUntil($value); - - /** - * Take items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeWhile($value); - - /** - * Pass the collection to the given callback and then return it. - * - * @param callable(TValue): mixed $callback - * @return $this - */ - public function tap(callable $callback); - - /** - * Pass the enumerable to the given callback and return the result. - * - * @template TPipeReturnType - * - * @param callable($this): TPipeReturnType $callback - * @return TPipeReturnType - */ - public function pipe(callable $callback); - - /** - * Pass the collection into a new class. - * - * @template TPipeIntoValue - * - * @param class-string $class - * @return TPipeIntoValue - */ - public function pipeInto($class); - - /** - * Pass the collection through a series of callable pipes and return the result. - * - * @param array $pipes - * @return mixed - */ - public function pipeThrough($pipes); - - /** - * Get the values of a given key. - * - * @param string|array $value - * @param string|null $key - * @return static - */ - public function pluck($value, $key = null); - - /** - * Create a collection of all elements that do not pass a given truth test. - * - * @param (callable(TValue, TKey): bool)|bool|TValue $callback - * @return static - */ - public function reject($callback = true); - - /** - * Convert a flatten "dot" notation array into an expanded array. - * - * @return static - */ - public function undot(); - - /** - * Return only unique items from the collection array. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @param bool $strict - * @return static - */ - public function unique($key = null, $strict = false); - - /** - * Return only unique items from the collection array using strict comparison. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @return static - */ - public function uniqueStrict($key = null); - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values(); - - /** - * Pad collection to the specified length with a value. - * - * @template TPadValue - * - * @param int $size - * @param TPadValue $value - * @return static - */ - public function pad($size, $value); - - /** - * Get the values iterator. - * - * @return \Traversable - */ - public function getIterator(): Traversable; - - /** - * Count the number of items in the collection. - * - * @return int - */ - public function count(): int; - - /** - * Count the number of items in the collection by a field or using a callback. - * - * @param (callable(TValue, TKey): (array-key|\UnitEnum))|string|null $countBy - * @return static - */ - public function countBy($countBy = null); - - /** - * Zip the collection together with one or more arrays. - * - * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); - * => [[1, 4], [2, 5], [3, 6]] - * - * @template TZipValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items - * @return static> - */ - public function zip($items); - - /** - * Collect the values into a collection. - * - * @return \Illuminate\Support\Collection - */ - public function collect(); - - /** - * Get the collection of items as a plain array. - * - * @return array - */ - public function toArray(); - - /** - * Convert the object into something JSON serializable. - * - * @return mixed - */ - public function jsonSerialize(): mixed; - - /** - * Get the collection of items as JSON. - * - * @param int $options - * @return string - */ - public function toJson($options = 0); - - /** - * Get the collection of items as pretty print formatted JSON. - * - * @param int $options - * @return string - */ - public function toPrettyJson(int $options = 0); - - /** - * Get a CachingIterator instance. - * - * @param int $flags - * @return \CachingIterator - */ - public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING); - - /** - * Convert the collection to its string representation. - * - * @return string - */ - public function __toString(); - - /** - * Indicate that the model's string representation should be escaped when __toString is invoked. - * - * @param bool $escape - * @return $this - */ - public function escapeWhenCastingToString($escape = true); - - /** - * Add a method to the list of proxied methods. - * - * @param string $method - * @return void - */ - public static function proxy($method); - - /** - * Dynamically access collection proxies. - * - * @param string $key - * @return mixed - * - * @throws \Exception - */ - public function __get($key); -} diff --git a/src/Illuminate/Support/HigherOrderCollectionProxy.php b/src/Illuminate/Support/HigherOrderCollectionProxy.php deleted file mode 100644 index 035d0fda..00000000 --- a/src/Illuminate/Support/HigherOrderCollectionProxy.php +++ /dev/null @@ -1,69 +0,0 @@ - - * @mixin TValue - */ -class HigherOrderCollectionProxy -{ - /** - * The collection being operated on. - * - * @var \Illuminate\Support\Enumerable - */ - protected $collection; - - /** - * The method being proxied. - * - * @var string - */ - protected $method; - - /** - * Create a new proxy instance. - * - * @param \Illuminate\Support\Enumerable $collection - * @param string $method - */ - public function __construct(Enumerable $collection, $method) - { - $this->method = $method; - $this->collection = $collection; - } - - /** - * Proxy accessing an attribute onto the collection items. - * - * @param string $key - * @return mixed - */ - public function __get($key) - { - return $this->collection->{$this->method}(function ($value) use ($key) { - return is_array($value) ? $value[$key] : $value->{$key}; - }); - } - - /** - * Proxy a method call onto the collection items. - * - * @param string $method - * @param array $parameters - * @return mixed - */ - public function __call($method, $parameters) - { - return $this->collection->{$this->method}(function ($value) use ($method, $parameters) { - return is_string($value) - ? $value::{$method}(...$parameters) - : $value->{$method}(...$parameters); - }); - } -} diff --git a/src/Illuminate/Support/ItemNotFoundException.php b/src/Illuminate/Support/ItemNotFoundException.php deleted file mode 100644 index 05a51d95..00000000 --- a/src/Illuminate/Support/ItemNotFoundException.php +++ /dev/null @@ -1,9 +0,0 @@ - - */ -class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable -{ - /** - * @use \Illuminate\Support\Traits\EnumeratesValues - */ - use EnumeratesValues, Macroable; - - /** - * The source from which to generate items. - * - * @var (Closure(): \Generator)|static|array - */ - public $source; - - /** - * Create a new lazy collection instance. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $source - * - * @throws \InvalidArgumentException - */ - public function __construct($source = null) - { - if ($source instanceof Closure || $source instanceof self) { - $this->source = $source; - } elseif (is_null($source)) { - $this->source = static::empty(); - } elseif ($source instanceof Generator) { - throw new InvalidArgumentException( - 'Generators should not be passed directly to LazyCollection. Instead, pass a generator function.' - ); - } else { - $this->source = $this->getArrayableItems($source); - } - } - - /** - * Create a new instance of the collection. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $items - * @return static - */ - protected function newInstance($items = []) - { - return new static($items); - } - - /** - * Create a new collection instance if the value isn't one already. - * - * @template TMakeKey of array-key - * @template TMakeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $items - * @return static - */ - public static function make($items = [], ...$args) - { - return new static($items, ...$args); - } - - /** - * Create a collection with the given range. - * - * @param int $from - * @param int $to - * @param int $step - * @return ($step is 0 ? never : static) - * - * @throws \InvalidArgumentException - */ - public static function range($from, $to, $step = 1, ...$args) - { - if ($step == 0) { - throw new InvalidArgumentException('Step value cannot be zero.'); - } - - return new static(function () use ($from, $to, $step) { - if ($from <= $to) { - for (; $from <= $to; $from += abs($step)) { - yield $from; - } - } else { - for (; $from >= $to; $from -= abs($step)) { - yield $from; - } - } - }); - } - - /** - * Get all items in the enumerable. - * - * @return array - */ - public function all() - { - if (is_array($this->source)) { - return $this->source; - } - - return iterator_to_array($this->getIterator()); - } - - /** - * Eager load all items into a new lazy collection backed by an array. - * - * @return static - */ - public function eager() - { - return new static($this->all()); - } - - /** - * Cache values as they're enumerated. - * - * @return static - */ - public function remember() - { - $iterator = $this->getIterator(); - - $iteratorIndex = 0; - - $cache = []; - - return new static(function () use ($iterator, &$iteratorIndex, &$cache) { - for ($index = 0; true; $index++) { - if (array_key_exists($index, $cache)) { - yield $cache[$index][0] => $cache[$index][1]; - - continue; - } - - if ($iteratorIndex < $index) { - $iterator->next(); - - $iteratorIndex++; - } - - if (! $iterator->valid()) { - break; - } - - $cache[$index] = [$iterator->key(), $iterator->current()]; - - yield $cache[$index][0] => $cache[$index][1]; - } - }); - } - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return float|int|null - */ - public function median($key = null) - { - return $this->collect()->median($key); - } - - /** - * Get the mode of a given key. - * - * @param string|array|null $key - * @return array|null - */ - public function mode($key = null) - { - return $this->collect()->mode($key); - } - - /** - * Collapse the collection of items into a single array. - * - * @return static - */ - public function collapse() - { - return new static(function () { - foreach ($this as $values) { - if (is_array($values) || $values instanceof Enumerable) { - foreach ($values as $value) { - yield $value; - } - } - } - }); - } - - /** - * Collapse the collection of items into a single array while preserving its keys. - * - * @return static - */ - public function collapseWithKeys() - { - return new static(function () { - foreach ($this as $values) { - if (is_array($values) || $values instanceof Enumerable) { - foreach ($values as $key => $value) { - yield $key => $value; - } - } - } - }); - } - - /** - * Determine if an item exists in the enumerable. - * - * @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 && $this->useAsCallable($key)) { - $placeholder = new stdClass; - - /** @var callable $key */ - return $this->first($key, $placeholder) !== $placeholder; - } - - if (func_num_args() === 1) { - $needle = $key; - - foreach ($this as $value) { - if ($value == $needle) { - return true; - } - } - - return false; - } - - return $this->contains($this->operatorForWhere(...func_get_args())); - } - - /** - * Determine if an item exists, using strict comparison. - * - * @param (callable(TValue): bool)|TValue|array-key $key - * @param TValue|null $value - * @return bool - */ - public function containsStrict($key, $value = null) - { - if (func_num_args() === 2) { - return $this->contains(fn ($item) => data_get($item, $key) === $value); - } - - if ($this->useAsCallable($key)) { - return ! is_null($this->first($key)); - } - - foreach ($this as $item) { - if ($item === $key) { - return true; - } - } - - return false; - } - - /** - * Determine if an item is not contained in the enumerable. - * - * @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()); - } - - /** - * Determine if an item is not contained in the enumerable, using strict comparison. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function doesntContainStrict($key, $operator = null, $value = null) - { - return ! $this->containsStrict(...func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function crossJoin(...$arrays) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function countBy($countBy = null) - { - $countBy = is_null($countBy) - ? $this->identity() - : $this->valueRetriever($countBy); - - return new static(function () use ($countBy) { - $counts = []; - - foreach ($this as $key => $value) { - $group = enum_value($countBy($value, $key)); - - if (empty($counts[$group])) { - $counts[$group] = 0; - } - - $counts[$group]++; - } - - yield from $counts; - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diff($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffAssoc($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffAssocUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffKeys($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function diffKeysUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function duplicates($callback = null, $strict = false) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function duplicatesStrict($callback = null) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function except($keys) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Run a filter over each of the items. - * - * @param (callable(TValue, TKey): bool)|null $callback - * @return static - */ - public function filter(?callable $callback = null) - { - if (is_null($callback)) { - $callback = fn ($value) => (bool) $value; - } - - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - if ($callback($value, $key)) { - yield $key => $value; - } - } - }); - } - - /** - * Get the first item from the enumerable passing the given truth test. - * - * @template TFirstDefault - * - * @param (callable(TValue): bool)|null $callback - * @param TFirstDefault|(\Closure(): TFirstDefault) $default - * @return TValue|TFirstDefault - */ - public function first(?callable $callback = null, $default = null) - { - $iterator = $this->getIterator(); - - if (is_null($callback)) { - if (! $iterator->valid()) { - return value($default); - } - - return $iterator->current(); - } - - foreach ($iterator as $key => $value) { - if ($callback($value, $key)) { - return $value; - } - } - - return value($default); - } - - /** - * Get a flattened list of the items in the collection. - * - * @param int $depth - * @return static - */ - public function flatten($depth = INF) - { - $instance = new static(function () use ($depth) { - foreach ($this as $item) { - if (! is_array($item) && ! $item instanceof Enumerable) { - yield $item; - } elseif ($depth === 1) { - yield from $item; - } else { - yield from (new static($item))->flatten($depth - 1); - } - } - }); - - return $instance->values(); - } - - /** - * Flip the items in the collection. - * - * @return static - */ - public function flip() - { - return new static(function () { - foreach ($this as $key => $value) { - if (is_string($value) || is_int($value)) { - yield $value => $key; - } - } - }); - } - - /** - * Get an item by key. - * - * @template TGetDefault - * - * @param TKey|null $key - * @param TGetDefault|(\Closure(): TGetDefault) $default - * @return TValue|TGetDefault - */ - public function get($key, $default = null) - { - if (is_null($key)) { - return; - } - - foreach ($this as $outerKey => $outerValue) { - if ($outerKey == $key) { - return $outerValue; - } - } - - return value($default); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function groupBy($groupBy, $preserveKeys = false) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function keyBy($keyBy) - { - return new static(function () use ($keyBy) { - $keyBy = $this->valueRetriever($keyBy); - - foreach ($this as $key => $item) { - $resolvedKey = $keyBy($item, $key); - - if ($resolvedKey instanceof \UnitEnum) { - $resolvedKey = enum_value($resolvedKey); - } - - if (is_object($resolvedKey) || is_null($resolvedKey)) { - $resolvedKey = (string) $resolvedKey; - } - - yield $resolvedKey => $item; - } - }); - } - - /** - * Determine if an item exists in the collection by key. - * - * @param mixed $key - * @return bool - */ - public function has($key) - { - $keys = array_flip(is_array($key) ? $key : func_get_args()); - - foreach ($this as $key => $value) { - unset($keys[$key]); - - if (empty($keys)) { - return true; - } - } - - return false; - } - - /** - * Determine if any of the keys exist in the collection. - * - * @param mixed $key - * @return bool - */ - public function hasAny($key) - { - $keys = array_flip(is_array($key) ? $key : func_get_args()); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $keys)) { - return true; - } - } - - return false; - } - - /** - * Concatenate values of a given key as a string. - * - * @param (callable(TValue, TKey): mixed)|string $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null) - { - return $this->collect()->implode(...func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersect($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersectUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersectAssoc($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersectAssocUsing($items, callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function intersectByKeys($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Determine if the items are empty or not. - * - * @return bool - */ - public function isEmpty() - { - return ! $this->getIterator()->valid(); - } - - /** - * Determine if the collection contains a single item. - * - * @param (callable(TValue, TKey): bool)|null $callback - * @return bool - * - * @deprecated 12.49.0 Use the `hasSole()` method instead. - */ - public function containsOneItem(?callable $callback = null): bool - { - return $this->hasSole($callback); - } - - /** - * Determine if the collection contains multiple items. - * - * @return bool - * - * @deprecated 12.50.0 Use the `hasMany()` method instead. - */ - public function containsManyItems(): bool - { - return $this->hasMany(); - } - - /** - * 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 = '') - { - return $this->collect()->join(...func_get_args()); - } - - /** - * Get the keys of the collection items. - * - * @return static - */ - public function keys() - { - return new static(function () { - foreach ($this as $key => $value) { - yield $key; - } - }); - } - - /** - * 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) - { - $needle = $placeholder = new stdClass; - - foreach ($this as $key => $value) { - if (is_null($callback) || $callback($value, $key)) { - $needle = $value; - } - } - - return $needle === $placeholder ? value($default) : $needle; - } - - /** - * Get the values of a given key. - * - * @param string|array $value - * @param string|null $key - * @return static - */ - public function pluck($value, $key = null) - { - return new static(function () use ($value, $key) { - [$value, $key] = $this->explodePluckParameters($value, $key); - - foreach ($this as $item) { - $itemValue = $value instanceof Closure - ? $value($item) - : data_get($item, $value); - - if (is_null($key)) { - yield $itemValue; - } else { - $itemKey = $key instanceof Closure - ? $key($item) - : data_get($item, $key); - - if (is_object($itemKey) && method_exists($itemKey, '__toString')) { - $itemKey = (string) $itemKey; - } - - yield $itemKey => $itemValue; - } - } - }); - } - - /** - * 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(function () use ($callback) { - foreach ($this as $key => $value) { - yield $key => $callback($value, $key); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function mapToDictionary(callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * 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) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - yield from $callback($value, $key); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function merge($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function mergeRecursive($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Multiply the items in the collection by the multiplier. - * - * @param int $multiplier - * @return static - */ - public function multiply(int $multiplier) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Create a collection by using this collection for keys and another for its values. - * - * @template TCombineValue - * - * @param \IteratorAggregate|array|(callable(): \Generator) $values - * @return static - */ - public function combine($values) - { - return new static(function () use ($values) { - $values = $this->makeIterator($values); - - $errorMessage = 'Both parameters should have an equal number of elements'; - - foreach ($this as $key) { - if (! $values->valid()) { - trigger_error($errorMessage, E_USER_WARNING); - - break; - } - - yield $key => $values->current(); - - $values->next(); - } - - if ($values->valid()) { - trigger_error($errorMessage, E_USER_WARNING); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function union($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return ($step is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function nth($step, $offset = 0) - { - if ($step < 1) { - throw new InvalidArgumentException('Step value must be at least 1.'); - } - - return new static(function () use ($step, $offset) { - $position = 0; - - foreach ($this->slice($offset) as $item) { - if ($position % $step === 0) { - yield $item; - } - - $position++; - } - }); - } - - /** - * Get the items with the specified keys. - * - * @param \Illuminate\Support\Enumerable|array|string $keys - * @return static - */ - public function only($keys) - { - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } elseif (! is_null($keys)) { - $keys = is_array($keys) ? $keys : func_get_args(); - } - - return new static(function () use ($keys) { - if (is_null($keys)) { - yield from $this; - } else { - $keys = array_flip($keys); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $keys)) { - yield $key => $value; - - unset($keys[$key]); - - if (empty($keys)) { - break; - } - } - } - } - }); - } - - /** - * Select specific values from the items within the collection. - * - * @param \Illuminate\Support\Enumerable|array|string $keys - * @return static - */ - public function select($keys) - { - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } elseif (! is_null($keys)) { - $keys = is_array($keys) ? $keys : func_get_args(); - } - - return new static(function () use ($keys) { - if (is_null($keys)) { - yield from $this; - } else { - foreach ($this as $item) { - $result = []; - - foreach ($keys as $key) { - if (Arr::accessible($item) && Arr::exists($item, $key)) { - $result[$key] = $item[$key]; - } elseif (is_object($item) && isset($item->{$key})) { - $result[$key] = $item->{$key}; - } - } - - yield $result; - } - } - }); - } - - /** - * Push all of the given items onto the collection. - * - * @template TConcatKey of array-key - * @template TConcatValue - * - * @param iterable $source - * @return static - */ - public function concat($source) - { - return (new static(function () use ($source) { - yield from $this; - yield from $source; - }))->values(); - } - - /** - * Get one or a specified number of items randomly from the collection. - * - * @param int|null $number - * @param bool $preserveKeys - * @return ($number is null ? TValue : static) - * - * @throws \InvalidArgumentException - */ - public function random($number = null, $preserveKeys = false) - { - $result = $this->collect()->random(...func_get_args()); - - return is_null($number) ? $result : new static($result); - } - - /** - * Replace the collection items with the given items. - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable $items - * @return static - */ - public function replace($items) - { - return new static(function () use ($items) { - $items = $this->getArrayableItems($items); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $items)) { - yield $key => $items[$key]; - - unset($items[$key]); - } else { - yield $key => $value; - } - } - - foreach ($items as $key => $value) { - yield $key => $value; - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function replaceRecursive($items) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function reverse() - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * 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|false - */ - public function search($value, $strict = false) - { - /** @var (callable(TValue,TKey): bool) $predicate */ - $predicate = $this->useAsCallable($value) - ? $value - : function ($item) use ($value, $strict) { - return $strict ? $item === $value : $item == $value; - }; - - foreach ($this as $key => $item) { - if ($predicate($item, $key)) { - return $key; - } - } - - return false; - } - - /** - * Get the item before the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function before($value, $strict = false) - { - $previous = null; - - /** @var (callable(TValue,TKey): bool) $predicate */ - $predicate = $this->useAsCallable($value) - ? $value - : function ($item) use ($value, $strict) { - return $strict ? $item === $value : $item == $value; - }; - - foreach ($this as $key => $item) { - if ($predicate($item, $key)) { - return $previous; - } - - $previous = $item; - } - - return null; - } - - /** - * Get the item after the given item. - * - * @param TValue|(callable(TValue,TKey): bool) $value - * @param bool $strict - * @return TValue|null - */ - public function after($value, $strict = false) - { - $found = false; - - /** @var (callable(TValue,TKey): bool) $predicate */ - $predicate = $this->useAsCallable($value) - ? $value - : function ($item) use ($value, $strict) { - return $strict ? $item === $value : $item == $value; - }; - - foreach ($this as $key => $item) { - if ($found) { - return $item; - } - - if ($predicate($item, $key)) { - $found = true; - } - } - - return null; - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function shuffle() - { - return $this->passthru(__FUNCTION__, []); - } - - /** - * Create chunks representing a "sliding window" view of the items in the collection. - * - * @param positive-int $size - * @param positive-int $step - * @return static - * - * @throws \InvalidArgumentException - */ - public function sliding($size = 2, $step = 1) - { - if ($size < 1) { - throw new InvalidArgumentException('Size value must be at least 1.'); - } elseif ($step < 1) { - throw new InvalidArgumentException('Step value must be at least 1.'); - } - - return new static(function () use ($size, $step) { - $iterator = $this->getIterator(); - - $chunk = []; - - while ($iterator->valid()) { - $chunk[$iterator->key()] = $iterator->current(); - - if (count($chunk) == $size) { - yield (new static($chunk))->tap(function () use (&$chunk, $step) { - $chunk = array_slice($chunk, $step, null, true); - }); - - // If the $step between chunks is bigger than each chunk's $size - // we will skip the extra items (which should never be in any - // chunk) before we continue to the next chunk in the loop. - if ($step > $size) { - $skip = $step - $size; - - for ($i = 0; $i < $skip && $iterator->valid(); $i++) { - $iterator->next(); - } - } - } - - $iterator->next(); - } - }); - } - - /** - * Skip the first {$count} items. - * - * @param int $count - * @return static - */ - public function skip($count) - { - return new static(function () use ($count) { - $iterator = $this->getIterator(); - - while ($iterator->valid() && $count--) { - $iterator->next(); - } - - while ($iterator->valid()) { - yield $iterator->key() => $iterator->current(); - - $iterator->next(); - } - }); - } - - /** - * Skip items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function skipUntil($value) - { - $callback = $this->useAsCallable($value) ? $value : $this->equality($value); - - return $this->skipWhile($this->negate($callback)); - } - - /** - * Skip items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function skipWhile($value) - { - $callback = $this->useAsCallable($value) ? $value : $this->equality($value); - - return new static(function () use ($callback) { - $iterator = $this->getIterator(); - - while ($iterator->valid() && $callback($iterator->current(), $iterator->key())) { - $iterator->next(); - } - - while ($iterator->valid()) { - yield $iterator->key() => $iterator->current(); - - $iterator->next(); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function slice($offset, $length = null) - { - if ($offset < 0 || $length < 0) { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - $instance = $this->skip($offset); - - return is_null($length) ? $instance : $instance->take($length); - } - - /** - * {@inheritDoc} - * - * @throws \InvalidArgumentException - */ - #[\Override] - public function split($numberOfGroups) - { - if ($numberOfGroups < 1) { - throw new InvalidArgumentException('Number of groups must be at least 1.'); - } - - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. - * - * @param (callable(TValue, TKey): bool)|string|null $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; - - return $this - ->unless($filter == null) - ->filter($filter) - ->take(2) - ->collect() - ->sole(); - } - - /** - * Determine if the collection contains a single item or a single item matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasSole($key = null, $operator = null, $value = null): bool - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - return $this - ->unless($filter == null) - ->filter($filter) - ->take(2) - ->count() === 1; - } - - /** - * Get the first item in the collection but throw an exception if no matching items exist. - * - * @param (callable(TValue, TKey): bool)|string|null $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; - - return $this - ->unless($filter == null) - ->filter($filter) - ->take(1) - ->collect() - ->firstOrFail(); - } - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @param bool $preserveKeys - * @return ($preserveKeys is true ? static : static>) - */ - public function chunk($size, $preserveKeys = true) - { - if ($size <= 0) { - return static::empty(); - } - - $add = match ($preserveKeys) { - true => fn (array &$chunk, Traversable $iterator) => $chunk[$iterator->key()] = $iterator->current(), - false => fn (array &$chunk, Traversable $iterator) => $chunk[] = $iterator->current(), - }; - - return new static(function () use ($size, $add) { - $iterator = $this->getIterator(); - - while ($iterator->valid()) { - $chunk = []; - - while (true) { - $add($chunk, $iterator); - - if (count($chunk) < $size) { - $iterator->next(); - - if (! $iterator->valid()) { - break; - } - } else { - break; - } - } - - yield new static($chunk); - - $iterator->next(); - } - }); - } - - /** - * Split a collection into a certain number of groups, and fill the first groups completely. - * - * @param int $numberOfGroups - * @return ($numberOfGroups is positive-int ? static : never) - * - * @throws \InvalidArgumentException - */ - public function splitIn($numberOfGroups) - { - if ($numberOfGroups < 1) { - throw new InvalidArgumentException('Number of groups must be at least 1.'); - } - - return $this->chunk((int) ceil($this->count() / $numberOfGroups)); - } - - /** - * Chunk the collection into chunks with a callback. - * - * @param callable(TValue, TKey, Collection): bool $callback - * @return static> - */ - public function chunkWhile(callable $callback) - { - return new static(function () use ($callback) { - $iterator = $this->getIterator(); - - $chunk = new Collection; - - if ($iterator->valid()) { - $chunk[$iterator->key()] = $iterator->current(); - - $iterator->next(); - } - - while ($iterator->valid()) { - if (! $callback($iterator->current(), $iterator->key(), $chunk)) { - yield new static($chunk); - - $chunk = new Collection; - } - - $chunk[$iterator->key()] = $iterator->current(); - - $iterator->next(); - } - - if ($chunk->isNotEmpty()) { - yield new static($chunk); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sort($callback = null) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortDesc($options = SORT_REGULAR) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortBy($callback, $options = SORT_REGULAR, $descending = false) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortByDesc($callback, $options = SORT_REGULAR) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortKeys($options = SORT_REGULAR, $descending = false) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortKeysDesc($options = SORT_REGULAR) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function sortKeysUsing(callable $callback) - { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - /** - * Take the first or last {$limit} items. - * - * @param int $limit - * @return static - */ - public function take($limit) - { - if ($limit < 0) { - return new static(function () use ($limit) { - $limit = abs($limit); - $ringBuffer = []; - $position = 0; - - foreach ($this as $key => $value) { - $ringBuffer[$position] = [$key, $value]; - $position = ($position + 1) % $limit; - } - - for ($i = 0, $end = min($limit, count($ringBuffer)); $i < $end; $i++) { - $pointer = ($position + $i) % $limit; - yield $ringBuffer[$pointer][0] => $ringBuffer[$pointer][1]; - } - }); - } - - return new static(function () use ($limit) { - $iterator = $this->getIterator(); - - while ($limit--) { - if (! $iterator->valid()) { - break; - } - - yield $iterator->key() => $iterator->current(); - - if ($limit) { - $iterator->next(); - } - } - }); - } - - /** - * Take items in the collection until the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeUntil($value) - { - /** @var callable(TValue, TKey): bool $callback */ - $callback = $this->useAsCallable($value) ? $value : $this->equality($value); - - return new static(function () use ($callback) { - foreach ($this as $key => $item) { - if ($callback($item, $key)) { - break; - } - - yield $key => $item; - } - }); - } - - /** - * Take items in the collection until a given point in time, with an optional callback on timeout. - * - * @param \DateTimeInterface $timeout - * @param callable(TValue|null, TKey|null): mixed|null $callback - * @return static - */ - public function takeUntilTimeout(DateTimeInterface $timeout, ?callable $callback = null) - { - $timeout = $timeout->getTimestamp(); - - return new static(function () use ($timeout, $callback) { - if ($this->now() >= $timeout) { - if ($callback) { - $callback(null, null); - } - - return; - } - - foreach ($this as $key => $value) { - yield $key => $value; - - if ($this->now() >= $timeout) { - if ($callback) { - $callback($value, $key); - } - - break; - } - } - }); - } - - /** - * Take items in the collection while the given condition is met. - * - * @param TValue|callable(TValue,TKey): bool $value - * @return static - */ - public function takeWhile($value) - { - /** @var callable(TValue, TKey): bool $callback */ - $callback = $this->useAsCallable($value) ? $value : $this->equality($value); - - return $this->takeUntil(fn ($item, $key) => ! $callback($item, $key)); - } - - /** - * Pass each item in the collection to the given callback, lazily. - * - * @param callable(TValue, TKey): mixed $callback - * @return static - */ - public function tapEach(callable $callback) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - $callback($value, $key); - - yield $key => $value; - } - }); - } - - /** - * Throttle the values, releasing them at most once per the given seconds. - * - * @return static - */ - public function throttle(float $seconds) - { - return new static(function () use ($seconds) { - $microseconds = $seconds * 1_000_000; - - foreach ($this as $key => $value) { - $fetchedAt = $this->preciseNow(); - - yield $key => $value; - - $sleep = $microseconds - ($this->preciseNow() - $fetchedAt); - - $this->usleep((int) $sleep); - } - }); - } - - /** - * Flatten a multi-dimensional associative array with dots. - * - * @param int $depth - * @return static - */ - public function dot($depth = INF) - { - return $this->passthru(__FUNCTION__, [$depth]); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function undot() - { - return $this->passthru(__FUNCTION__, []); - } - - /** - * Return only unique items from the collection array. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @param bool $strict - * @return static - */ - public function unique($key = null, $strict = false) - { - $callback = $this->valueRetriever($key); - - return new static(function () use ($callback, $strict) { - $exists = []; - - foreach ($this as $key => $item) { - if (! in_array($id = $callback($item, $key), $exists, $strict)) { - yield $key => $item; - - $exists[] = $id; - } - } - }); - } - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values() - { - return new static(function () { - foreach ($this as $item) { - yield $item; - } - }); - } - - /** - * Run the given callback every time the interval has passed. - * - * @return static - */ - public function withHeartbeat(DateInterval|int $interval, callable $callback) - { - $seconds = is_int($interval) ? $interval : $this->intervalSeconds($interval); - - return new static(function () use ($seconds, $callback) { - $start = $this->now(); - - foreach ($this as $key => $value) { - $now = $this->now(); - - if (($now - $start) >= $seconds) { - $callback(); - - $start = $now; - } - - yield $key => $value; - } - }); - } - - /** - * Get the total seconds from the given interval. - */ - protected function intervalSeconds(DateInterval $interval): int - { - $start = new DateTimeImmutable(); - - return $start->add($interval)->getTimestamp() - $start->getTimestamp(); - } - - /** - * Zip the collection together with one or more arrays. - * - * e.g. new LazyCollection([1, 2, 3])->zip([4, 5, 6]); - * => [[1, 4], [2, 5], [3, 6]] - * - * @template TZipValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items - * @return static> - */ - public function zip($items) - { - $iterables = func_get_args(); - - return new static(function () use ($iterables) { - $iterators = (new Collection($iterables)) - ->map(fn ($iterable) => $this->makeIterator($iterable)) - ->prepend($this->getIterator()); - - while ($iterators->contains->valid()) { - yield new static($iterators->map->current()); - - $iterators->each->next(); - } - }); - } - - /** - * {@inheritDoc} - */ - #[\Override] - public function pad($size, $value) - { - if ($size < 0) { - return $this->passthru(__FUNCTION__, func_get_args()); - } - - return new static(function () use ($size, $value) { - $yielded = 0; - - foreach ($this as $index => $item) { - yield $index => $item; - - $yielded++; - } - - while ($yielded++ < $size) { - yield $value; - } - }); - } - - /** - * Get the values iterator. - * - * @return \Traversable - */ - public function getIterator(): Traversable - { - return $this->makeIterator($this->source); - } - - /** - * Count the number of items in the collection. - * - * @return int - */ - public function count(): int - { - if (is_array($this->source)) { - return count($this->source); - } - - return iterator_count($this->getIterator()); - } - - /** - * Make an iterator from the given source. - * - * @template TIteratorKey of array-key - * @template TIteratorValue - * - * @param \IteratorAggregate|array|(callable(): \Generator) $source - * @return \Traversable - */ - protected function makeIterator($source) - { - if ($source instanceof IteratorAggregate) { - return $source->getIterator(); - } - - if (is_array($source)) { - return new ArrayIterator($source); - } - - if (is_callable($source)) { - $maybeTraversable = $source(); - - return $maybeTraversable instanceof Traversable - ? $maybeTraversable - : new ArrayIterator(Arr::wrap($maybeTraversable)); - } - - return new ArrayIterator((array) $source); - } - - /** - * Explode the "value" and "key" arguments passed to "pluck". - * - * @param string|string[] $value - * @param string|string[]|null $key - * @return array{string[],string[]|null} - */ - protected function explodePluckParameters($value, $key) - { - $value = is_string($value) ? explode('.', $value) : $value; - - $key = is_null($key) || is_array($key) || $key instanceof Closure ? $key : explode('.', $key); - - return [$value, $key]; - } - - /** - * Pass this lazy collection through a method on the collection class. - * - * @param string $method - * @param array $params - * @return static - */ - protected function passthru($method, array $params) - { - return new static(function () use ($method, $params) { - yield from $this->collect()->$method(...$params); - }); - } - - /** - * Get the current time. - * - * @return int - */ - protected function now() - { - return class_exists(Carbon::class) - ? Carbon::now()->getTimestamp() - : time(); - } - - /** - * Get the precise current time. - * - * @return float - */ - protected function preciseNow() - { - return class_exists(Carbon::class) - ? Carbon::now()->getPreciseTimestamp() - : microtime(true) * 1_000_000; - } - - /** - * Sleep for the given amount of microseconds. - * - * @return void - */ - protected function usleep(int $microseconds) - { - if ($microseconds <= 0) { - return; - } - - class_exists(Sleep::class) - ? Sleep::usleep($microseconds) - : usleep($microseconds); - } -} diff --git a/src/Illuminate/Support/MultipleItemsFoundException.php b/src/Illuminate/Support/MultipleItemsFoundException.php deleted file mode 100644 index 9c5c7c56..00000000 --- a/src/Illuminate/Support/MultipleItemsFoundException.php +++ /dev/null @@ -1,39 +0,0 @@ -count = $count; - - parent::__construct("$count items were found.", $code, $previous); - } - - /** - * Get the number of items found. - * - * @return int - */ - public function getCount() - { - return $this->count; - } -} diff --git a/src/Illuminate/Support/Traits/EnumeratesValues.php b/src/Illuminate/Support/Traits/EnumeratesValues.php deleted file mode 100644 index fbd7c90d..00000000 --- a/src/Illuminate/Support/Traits/EnumeratesValues.php +++ /dev/null @@ -1,1244 +0,0 @@ - $average - * @property-read HigherOrderCollectionProxy $avg - * @property-read HigherOrderCollectionProxy $contains - * @property-read HigherOrderCollectionProxy $doesntContain - * @property-read HigherOrderCollectionProxy $each - * @property-read HigherOrderCollectionProxy $every - * @property-read HigherOrderCollectionProxy $filter - * @property-read HigherOrderCollectionProxy $first - * @property-read HigherOrderCollectionProxy $flatMap - * @property-read HigherOrderCollectionProxy $groupBy - * @property-read HigherOrderCollectionProxy $hasMany - * @property-read HigherOrderCollectionProxy $hasSole - * @property-read HigherOrderCollectionProxy $keyBy - * @property-read HigherOrderCollectionProxy $last - * @property-read HigherOrderCollectionProxy $map - * @property-read HigherOrderCollectionProxy $max - * @property-read HigherOrderCollectionProxy $min - * @property-read HigherOrderCollectionProxy $partition - * @property-read HigherOrderCollectionProxy $percentage - * @property-read HigherOrderCollectionProxy $reject - * @property-read HigherOrderCollectionProxy $skipUntil - * @property-read HigherOrderCollectionProxy $skipWhile - * @property-read HigherOrderCollectionProxy $some - * @property-read HigherOrderCollectionProxy $sortBy - * @property-read HigherOrderCollectionProxy $sortByDesc - * @property-read HigherOrderCollectionProxy $sum - * @property-read HigherOrderCollectionProxy $takeUntil - * @property-read HigherOrderCollectionProxy $takeWhile - * @property-read HigherOrderCollectionProxy $unique - * @property-read HigherOrderCollectionProxy $unless - * @property-read HigherOrderCollectionProxy $until - * @property-read HigherOrderCollectionProxy $when - */ -trait EnumeratesValues -{ - use Conditionable; - - /** - * Indicates that the object's string representation should be escaped when __toString is invoked. - * - * @var bool - */ - protected $escapeWhenCastingToString = false; - - /** - * The methods that can be proxied. - * - * @var array - */ - protected static $proxies = [ - 'average', - 'avg', - 'contains', - 'doesntContain', - 'each', - 'every', - 'filter', - 'first', - 'flatMap', - 'groupBy', - 'hasMany', - 'hasSole', - 'keyBy', - 'last', - 'map', - 'max', - 'min', - 'partition', - 'percentage', - 'reject', - 'skipUntil', - 'skipWhile', - 'some', - 'sortBy', - 'sortByDesc', - 'sum', - 'takeUntil', - 'takeWhile', - 'unique', - 'unless', - 'until', - 'when', - ]; - - /** - * Create a new collection instance if the value isn't one already. - * - * @template TMakeKey of array-key - * @template TMakeValue - * - * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items - * @return static - */ - public static function make($items = [], ...$args) - { - return new static($items, ...$args); - } - - /** - * Wrap the given value in a collection if applicable. - * - * @template TWrapValue - * - * @param iterable|TWrapValue $value - * @return static - */ - public static function wrap($value, ...$args) - { - return $value instanceof Enumerable - ? new static($value, ...$args) - : new static(Arr::wrap($value), ...$args); - } - - /** - * Get the underlying items from the given collection if applicable. - * - * @template TUnwrapKey of array-key - * @template TUnwrapValue - * - * @param array|static $value - * @return array - */ - public static function unwrap($value) - { - return $value instanceof Enumerable ? $value->all() : $value; - } - - /** - * Create a new instance with no items. - * - * @return static - */ - public static function empty(...$args) - { - return new static([], ...$args); - } - - /** - * Create a new collection by invoking the callback a given amount of times. - * - * @template TTimesValue - * - * @param int $number - * @param (callable(int): TTimesValue)|null $callback - * @return static - */ - public static function times($number, ?callable $callback = null, ...$args) - { - if ($number < 1) { - return new static([], ...$args); - } - - return static::range(1, $number, 1, ...$args) - ->unless($callback == null) - ->map($callback); - } - - /** - * Create a new collection by decoding a JSON string. - * - * @param string $json - * @param int $depth - * @param int $flags - * @return static - */ - public static function fromJson($json, $depth = 512, $flags = 0, ...$args) - { - return new static(json_decode($json, true, $depth, $flags), ...$args); - } - - /** - * 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); - - $reduced = $this->reduce(static function (&$reduce, $value) use ($callback) { - if (! is_null($resolved = $callback($value))) { - $reduce[0] += $resolved; - $reduce[1]++; - } - - return $reduce; - }, [0, 0]); - - return $reduced[1] ? $reduced[0] / $reduced[1] : null; - } - - /** - * Alias for the "avg" method. - * - * @param (callable(TValue): float|int)|string|null $callback - * @return float|int|null - */ - public function average($callback = null) - { - return $this->avg($callback); - } - - /** - * Alias for the "contains" method. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function some($key, $operator = null, $value = null) - { - return $this->contains(...func_get_args()); - } - - /** - * Dump the given arguments and terminate execution. - * - * @param mixed ...$args - * @return never - */ - public function dd(...$args) - { - dd($this->all(), ...$args); - } - - /** - * Dump the items. - * - * @param mixed ...$args - * @return $this - */ - public function dump(...$args) - { - dump($this->all(), ...$args); - - return $this; - } - - /** - * Execute a callback over each item. - * - * @param callable(TValue, TKey): mixed $callback - * @return $this - */ - public function each(callable $callback) - { - foreach ($this as $key => $item) { - if ($callback($item, $key) === false) { - break; - } - } - - return $this; - } - - /** - * Execute a callback over each nested chunk of items. - * - * @param callable(...mixed): mixed $callback - * @return static - */ - public function eachSpread(callable $callback) - { - return $this->each(function ($chunk, $key) use ($callback) { - $chunk[] = $key; - - return $callback(...$chunk); - }); - } - - /** - * Determine if all items pass the given truth test. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function every($key, $operator = null, $value = null) - { - if (func_num_args() === 1) { - $callback = $this->valueRetriever($key); - - foreach ($this as $k => $v) { - if (! $callback($v, $k)) { - return false; - } - } - - return true; - } - - return $this->every($this->operatorForWhere(...func_get_args())); - } - - /** - * Get the first item by the given key value pair. - * - * @param callable|string $key - * @param mixed $operator - * @param mixed $value - * @return TValue|null - */ - public function firstWhere($key, $operator = null, $value = null) - { - return $this->first($this->operatorForWhere(...func_get_args())); - } - - /** - * Determine if the collection contains multiple items, optionally matching the given criteria. - * - * @param (callable(TValue, TKey): bool)|string|null $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function hasMany($key = null, $operator = null, $value = null): bool - { - $filter = func_num_args() > 1 - ? $this->operatorForWhere(...func_get_args()) - : $key; - - return $this - ->unless($filter == null) - ->filter($filter) - ->take(2) - ->count() === 2; - } - - /** - * Get a single key's value from the first matching item in the collection. - * - * @template TValueDefault - * - * @param string $key - * @param TValueDefault|(\Closure(): TValueDefault) $default - * @return TValue|TValueDefault - */ - public function value($key, $default = null) - { - $value = $this->first(function ($target) use ($key) { - return data_has($target, $key); - }); - - return data_get($value, $key, $default); - } - - /** - * Ensure that every item in the collection is of the expected type. - * - * @template TEnsureOfType - * - * @param class-string|array>|'string'|'int'|'float'|'bool'|'array'|'null' $type - * @return static - * - * @throws \UnexpectedValueException - */ - public function ensure($type) - { - $allowedTypes = is_array($type) ? $type : [$type]; - - return $this->each(function ($item, $index) use ($allowedTypes) { - $itemType = get_debug_type($item); - - foreach ($allowedTypes as $allowedType) { - if ($itemType === $allowedType || $item instanceof $allowedType) { - return true; - } - } - - throw new UnexpectedValueException( - sprintf("Collection should only include [%s] items, but '%s' found at position %d.", implode(', ', $allowedTypes), $itemType, $index) - ); - }); - } - - /** - * Determine if the collection is not empty. - * - * @phpstan-assert-if-true TValue $this->first() - * @phpstan-assert-if-true TValue $this->last() - * - * @phpstan-assert-if-false null $this->first() - * @phpstan-assert-if-false null $this->last() - * - * @return bool - */ - public function isNotEmpty() - { - return ! $this->isEmpty(); - } - - /** - * Run a map over each nested chunk of items. - * - * @template TMapSpreadValue - * - * @param callable(mixed...): TMapSpreadValue $callback - * @return static - */ - public function mapSpread(callable $callback) - { - return $this->map(function ($chunk, $key) use ($callback) { - $chunk[] = $key; - - return $callback(...$chunk); - }); - } - - /** - * Run a grouping map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @template TMapToGroupsKey of array-key - * @template TMapToGroupsValue - * - * @param callable(TValue, TKey): array $callback - * @return static> - */ - public function mapToGroups(callable $callback) - { - $groups = $this->mapToDictionary($callback); - - return $groups->map($this->make(...)); - } - - /** - * Map a collection and flatten the result by a single level. - * - * @template TFlatMapKey of array-key - * @template TFlatMapValue - * - * @param callable(TValue, TKey): (\Illuminate\Support\Collection|array) $callback - * @return static - */ - public function flatMap(callable $callback) - { - return $this->map($callback)->collapse(); - } - - /** - * Map the values into a new class. - * - * @template TMapIntoValue - * - * @param class-string $class - * @return static - */ - public function mapInto($class) - { - if (is_subclass_of($class, BackedEnum::class)) { - return $this->map(fn ($value, $key) => $class::from($value)); - } - - return $this->map(fn ($value, $key) => new $class($value, $key)); - } - - /** - * Get the min value of a given key. - * - * @template TMinResult = mixed - * - * @param (callable(TValue): TMinResult)|string|null $callback - * @return ($callback is callable ? ?TMinResult : ($callback is null ? ?TValue : mixed)) - */ - public function min($callback = null) - { - $callback = $this->valueRetriever($callback); - - return $this->map(fn ($value) => $callback($value)) - ->reject(fn ($value) => is_null($value)) - ->reduce(fn ($result, $value) => is_null($result) || $value < $result ? $value : $result); - } - - /** - * Get the max value of a given key. - * - * @template TMaxResult = mixed - * - * @param (callable(TValue): TMaxResult)|string|null $callback - * @return ($callback is callable ? ?TMaxResult : ($callback is null ? ?TValue : mixed)) - */ - public function max($callback = null) - { - $callback = $this->valueRetriever($callback); - - return $this->reject(fn ($value) => is_null($value))->reduce(function ($result, $item) use ($callback) { - $value = $callback($item); - - return is_null($result) || $value > $result ? $value : $result; - }); - } - - /** - * "Paginate" the collection by slicing it into a smaller collection. - * - * @param int $page - * @param int $perPage - * @return static - */ - public function forPage($page, $perPage) - { - $offset = max(0, ($page - 1) * $perPage); - - return $this->slice($offset, $perPage); - } - - /** - * Partition the collection into two arrays using the given callback or key. - * - * @param (callable(TValue, TKey): bool)|TValue|string $key - * @param mixed $operator - * @param mixed $value - * @return static, static> - */ - public function partition($key, $operator = null, $value = null) - { - $callback = func_num_args() === 1 - ? $this->valueRetriever($key) - : $this->operatorForWhere(...func_get_args()); - - [$passed, $failed] = Arr::partition($this->getIterator(), $callback); - - return $this->newInstance([$this->newInstance($passed), $this->newInstance($failed)]); - } - - /** - * Calculate the percentage of items that pass a given truth test. - * - * @param (callable(TValue, TKey): bool) $callback - * @param int $precision - * @return float|null - */ - public function percentage(callable $callback, int $precision = 2) - { - if ($this->isEmpty()) { - return null; - } - - return round( - $this->filter($callback)->count() / $this->count() * 100, - $precision - ); - } - - /** - * Get the sum of the given values. - * - * @template TReturnType - * - * @param (callable(TValue, TKey): TReturnType)|string|null $callback - * @return ($callback is callable ? TReturnType : mixed) - */ - public function sum($callback = null) - { - $callback = is_null($callback) - ? $this->identity() - : $this->valueRetriever($callback); - - return $this->reduce(fn ($result, $item, $key) => $result + $callback($item, $key), 0); - } - - /** - * Apply the callback if the collection is empty. - * - * @template TWhenEmptyReturnType - * - * @param (callable($this): TWhenEmptyReturnType) $callback - * @param (callable($this): TWhenEmptyReturnType)|null $default - * @return $this|TWhenEmptyReturnType - */ - public function whenEmpty(callable $callback, ?callable $default = null) - { - return $this->when($this->isEmpty(), $callback, $default); - } - - /** - * Apply the callback if the collection is not empty. - * - * @template TWhenNotEmptyReturnType - * - * @param callable($this): TWhenNotEmptyReturnType $callback - * @param (callable($this): TWhenNotEmptyReturnType)|null $default - * @return $this|TWhenNotEmptyReturnType - */ - public function whenNotEmpty(callable $callback, ?callable $default = null) - { - return $this->when($this->isNotEmpty(), $callback, $default); - } - - /** - * Apply the callback unless the collection is empty. - * - * @template TUnlessEmptyReturnType - * - * @param callable($this): TUnlessEmptyReturnType $callback - * @param (callable($this): TUnlessEmptyReturnType)|null $default - * @return $this|TUnlessEmptyReturnType - */ - public function unlessEmpty(callable $callback, ?callable $default = null) - { - return $this->whenNotEmpty($callback, $default); - } - - /** - * Apply the callback unless the collection is not empty. - * - * @template TUnlessNotEmptyReturnType - * - * @param callable($this): TUnlessNotEmptyReturnType $callback - * @param (callable($this): TUnlessNotEmptyReturnType)|null $default - * @return $this|TUnlessNotEmptyReturnType - */ - public function unlessNotEmpty(callable $callback, ?callable $default = null) - { - return $this->whenEmpty($callback, $default); - } - - /** - * Filter items by the given key value pair. - * - * @param callable|string $key - * @param mixed $operator - * @param mixed $value - * @return static - */ - public function where($key, $operator = null, $value = null) - { - return $this->filter($this->operatorForWhere(...func_get_args())); - } - - /** - * Filter items where the value for the given key is null. - * - * @param string|null $key - * @return static - */ - public function whereNull($key = null) - { - return $this->whereStrict($key, null); - } - - /** - * Filter items where the value for the given key is not null. - * - * @param string|null $key - * @return static - */ - public function whereNotNull($key = null) - { - return $this->where($key, '!==', null); - } - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param mixed $value - * @return static - */ - public function whereStrict($key, $value) - { - return $this->where($key, '===', $value); - } - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @param bool $strict - * @return static - */ - public function whereIn($key, $values, $strict = false) - { - $values = $this->getArrayableItems($values); - - return $this->filter(fn ($item) => in_array(data_get($item, $key), $values, $strict)); - } - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereInStrict($key, $values) - { - return $this->whereIn($key, $values, true); - } - - /** - * Filter items such that the value of the given key is between the given values. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereBetween($key, $values) - { - return $this->where($key, '>=', reset($values))->where($key, '<=', end($values)); - } - - /** - * Filter items such that the value of the given key is not between the given values. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereNotBetween($key, $values) - { - return $this->filter( - fn ($item) => data_get($item, $key) < reset($values) || data_get($item, $key) > end($values) - ); - } - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @param bool $strict - * @return static - */ - public function whereNotIn($key, $values, $strict = false) - { - $values = $this->getArrayableItems($values); - - return $this->reject(fn ($item) => in_array(data_get($item, $key), $values, $strict)); - } - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param \Illuminate\Contracts\Support\Arrayable|iterable $values - * @return static - */ - public function whereNotInStrict($key, $values) - { - return $this->whereNotIn($key, $values, true); - } - - /** - * Filter the items, removing any items that don't match the given type(s). - * - * @template TWhereInstanceOf - * - * @param class-string|array> $type - * @return static - */ - public function whereInstanceOf($type) - { - return $this->filter(function ($value) use ($type) { - if (is_array($type)) { - return array_any($type, fn ($classType) => $value instanceof $classType); - } - - return $value instanceof $type; - }); - } - - /** - * Pass the collection to the given callback and return the result. - * - * @template TPipeReturnType - * - * @param callable($this): TPipeReturnType $callback - * @return TPipeReturnType - */ - public function pipe(callable $callback) - { - return $callback($this); - } - - /** - * Pass the collection into a new class. - * - * @template TPipeIntoValue - * - * @param class-string $class - * @return TPipeIntoValue - */ - public function pipeInto($class) - { - return new $class($this); - } - - /** - * Pass the collection through a series of callable pipes and return the result. - * - * @param array $callbacks - * @return mixed - */ - public function pipeThrough($callbacks) - { - return (new Collection($callbacks))->reduce( - fn ($carry, $callback) => $callback($carry), - $this, - ); - } - - /** - * Reduce the collection to a single value. - * - * @template TReduceInitial - * @template TReduceReturnType - * - * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback - * @param TReduceInitial $initial - * @return TReduceInitial|TReduceReturnType - */ - public function reduce(callable $callback, $initial = null) - { - $result = $initial; - - foreach ($this as $key => $value) { - $result = $callback($result, $value, $key); - } - - return $result; - } - - /** - * Reduce the collection to a single value by mutating an initial value. - * - * @template TReduceIntoInitial - * - * @param TReduceIntoInitial $initial - * @param callable(TReduceIntoInitial, TValue, TKey): void $callback - * @return TReduceIntoInitial - */ - public function reduceInto($initial, callable $callback) - { - foreach ($this as $key => $value) { - $callback($initial, $value, $key); - } - - return $initial; - } - - /** - * Reduce the collection to multiple aggregate values. - * - * @param callable $callback - * @param mixed ...$initial - * @return array - * - * @throws \UnexpectedValueException - */ - public function reduceSpread(callable $callback, ...$initial) - { - $result = $initial; - - foreach ($this as $key => $value) { - $result = call_user_func_array($callback, array_merge($result, [$value, $key])); - - if (! is_array($result)) { - throw new UnexpectedValueException(sprintf( - "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.", - class_basename(static::class), gettype($result) - )); - } - } - - return $result; - } - - /** - * Reduce an associative collection to a single value. - * - * @template TReduceWithKeysInitial - * @template TReduceWithKeysReturnType - * - * @param callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue, TKey): TReduceWithKeysReturnType $callback - * @param TReduceWithKeysInitial $initial - * @return TReduceWithKeysInitial|TReduceWithKeysReturnType - */ - public function reduceWithKeys(callable $callback, $initial = null) - { - return $this->reduce($callback, $initial); - } - - /** - * Create a collection of all elements that do not pass a given truth test. - * - * @param (callable(TValue, TKey): bool)|bool|TValue $callback - * @return static - */ - public function reject($callback = true) - { - $useAsCallable = $this->useAsCallable($callback); - - return $this->filter(function ($value, $key) use ($callback, $useAsCallable) { - return $useAsCallable - ? ! $callback($value, $key) - : $value != $callback; - }); - } - - /** - * Chunk the collection into chunks by comparing adjacent values using the given key or callback. - * - * @param (callable(TValue, TKey): mixed)|string $key - * @return static> - */ - public function chunkBy($key) - { - $callback = $this->valueRetriever($key); - - return $this->chunkWhile( - fn ($value, $key, $chunk) => $callback($value, $key) == $callback($chunk->last(), $chunk->keys()->last()) - ); - } - - /** - * Pass the collection to the given callback and then return it. - * - * @param callable($this): mixed $callback - * @return $this - */ - public function tap(callable $callback) - { - $callback($this); - - return $this; - } - - /** - * Return only unique items from the collection array. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @param bool $strict - * @return static - */ - public function unique($key = null, $strict = false) - { - $callback = $this->valueRetriever($key); - - $exists = []; - - return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) { - if (in_array($id = $callback($item, $key), $exists, $strict)) { - return true; - } - - $exists[] = $id; - }); - } - - /** - * Return only unique items from the collection array using strict comparison. - * - * @param (callable(TValue, TKey): mixed)|string|null $key - * @return static - */ - public function uniqueStrict($key = null) - { - return $this->unique($key, true); - } - - /** - * Collect the values into a collection. - * - * @return \Illuminate\Support\Collection - */ - public function collect() - { - return new Collection($this->all()); - } - - /** - * Get the collection of items as a plain array. - * - * @return array - */ - public function toArray() - { - return $this->map(fn ($value) => $value instanceof Arrayable ? $value->toArray() : $value)->all(); - } - - /** - * Convert the object into something JSON serializable. - * - * @return array - */ - public function jsonSerialize(): array - { - return array_map(function ($value) { - return match (true) { - $value instanceof JsonSerializable => $value->jsonSerialize(), - $value instanceof Jsonable => json_decode($value->toJson(), true), - $value instanceof Arrayable => $value->toArray(), - default => $value, - }; - }, $this->all()); - } - - /** - * Get the collection of items as JSON. - * - * @param int $options - * @return string - */ - public function toJson($options = 0) - { - return json_encode($this->jsonSerialize(), $options); - } - - /** - * Get the collection of items as pretty print formatted JSON. - * - * @param int $options - * @return string - */ - public function toPrettyJson(int $options = 0) - { - return $this->toJson(JSON_PRETTY_PRINT | $options); - } - - /** - * Get a CachingIterator instance. - * - * @param int $flags - * @return \CachingIterator - */ - public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING) - { - return new CachingIterator($this->getIterator(), $flags); - } - - /** - * Convert the collection to its string representation. - * - * @return string - */ - public function __toString() - { - return $this->escapeWhenCastingToString - ? e($this->toJson()) - : $this->toJson(); - } - - /** - * Indicate that the model's string representation should be escaped when __toString is invoked. - * - * @param bool $escape - * @return $this - */ - public function escapeWhenCastingToString($escape = true) - { - $this->escapeWhenCastingToString = $escape; - - return $this; - } - - /** - * Add a method to the list of proxied methods. - * - * @param string $method - * @return void - */ - public static function proxy($method) - { - static::$proxies[] = $method; - } - - /** - * Dynamically access collection proxies. - * - * @param string $key - * @return mixed - * - * @throws \Exception - */ - public function __get($key) - { - if (! in_array($key, static::$proxies)) { - throw new Exception("Property [{$key}] does not exist on this collection instance."); - } - - return new HigherOrderCollectionProxy($this, $key); - } - - /** - * Results array of items from Collection or Arrayable. - * - * @param mixed $items - * @return array - */ - protected function getArrayableItems($items) - { - return is_null($items) || is_scalar($items) || $items instanceof UnitEnum - ? Arr::wrap($items) - : Arr::from($items); - } - - /** - * Get an operator checker callback. - * - * @param callable|string $key - * @param string|null $operator - * @param mixed $value - * @return \Closure - */ - protected function operatorForWhere($key, $operator = null, $value = null) - { - if ($this->useAsCallable($key)) { - return $key; - } - - if (func_num_args() === 1) { - $value = true; - - $operator = '='; - } - - if (func_num_args() === 2) { - $value = $operator; - - $operator = '='; - } - - return function ($item) use ($key, $operator, $value) { - $retrieved = enum_value(data_get($item, $key)); - $value = enum_value($value); - - $strings = array_filter([$retrieved, $value], function ($value) { - return match (true) { - is_string($value) => true, - $value instanceof \Stringable => true, - default => false, - }; - }); - - if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) { - return in_array($operator, ['!=', '<>', '!==']); - } - - switch ($operator) { - default: - case '=': - case '==': return $retrieved == $value; - case '!=': - case '<>': return $retrieved != $value; - case '<': return $retrieved < $value; - case '>': return $retrieved > $value; - case '<=': return $retrieved <= $value; - case '>=': return $retrieved >= $value; - case '===': return $retrieved === $value; - case '!==': return $retrieved !== $value; - case '<=>': return $retrieved <=> $value; - } - }; - } - - /** - * Determine if the given value is callable, but not a string. - * - * @param mixed $value - * @return bool - */ - protected function useAsCallable($value) - { - return ! is_string($value) && is_callable($value); - } - - /** - * Get a value retrieving callback. - * - * @param callable|string|null $value - * @return callable - */ - protected function valueRetriever($value) - { - if ($this->useAsCallable($value)) { - return $value; - } - - return fn ($item) => data_get($item, $value); - } - - /** - * Make a function to check an item's equality. - * - * @param mixed $value - * @return \Closure(mixed): bool - */ - protected function equality($value) - { - return fn ($item) => $item === $value; - } - - /** - * Make a function using another function, by negating its result. - * - * @param \Closure $callback - * @return \Closure - */ - protected function negate(Closure $callback) - { - return fn (...$params) => ! $callback(...$params); - } - - /** - * Make a function that returns what's passed to it. - * - * @return \Closure(TValue): TValue - */ - protected function identity() - { - return fn ($value) => $value; - } -} diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index d2eb3329..adc6e3a2 100755 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -105,7 +105,16 @@ function array_add($array, $key, $value) */ function array_build($array, Closure $callback) { - return Arr::build($array, $callback); + $results = array(); + + foreach ($array as $key => $value) + { + [$innerKey, $innerValue] = call_user_func($callback, $key, $value); + + $results[$innerKey] = $innerValue; + } + + return $results; } } @@ -164,7 +173,22 @@ function array_except($array, $keys) */ function array_fetch($array, $key) { - return Arr::fetch($array, $key); + foreach (explode('.', $key) as $segment) + { + $results = array(); + + foreach ($array as $value) + { + if (array_key_exists($segment, $value = (array) $value)) + { + $results[] = $value[$segment]; + } + } + + $array = array_values($results); + } + + return array_values($results); } } diff --git a/tests/Database/DatabaseEloquentCollectionTest.php b/tests/Database/DatabaseEloquentCollectionTest.php index f488bac9..e8b2d8cd 100755 --- a/tests/Database/DatabaseEloquentCollectionTest.php +++ b/tests/Database/DatabaseEloquentCollectionTest.php @@ -177,13 +177,13 @@ public function testCollectionReturnsUniqueItems() } - public function testLists() + public function testPluck() { $data = new Collection( [(object) ['name' => 'taylor', 'email' => 'foo'], (object) ['name' => 'dayle', 'email' => 'bar']] ); - $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->lists('email', 'name')); - $this->assertEquals(['foo', 'bar'], $data->lists('email')); + $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->pluck('email', 'name')->all()); + $this->assertEquals(['foo', 'bar'], $data->pluck('email')->all()); } diff --git a/tests/Support/SupportArrTest.php b/tests/Support/SupportArrTest.php index 164631d6..971fc2a2 100644 --- a/tests/Support/SupportArrTest.php +++ b/tests/Support/SupportArrTest.php @@ -155,7 +155,7 @@ public function testExists(): void $this->assertTrue(Arr::exists([null], 0)); $this->assertTrue(Arr::exists(['a' => 1], 'a')); $this->assertTrue(Arr::exists(['a' => null], 'a')); - $this->assertFalse(Arr::exists(new Collection(['a' => null]), 'a')); + $this->assertTrue(Arr::exists(new Collection(['a' => null]), 'a')); $this->assertFalse(Arr::exists([1], 1)); $this->assertFalse(Arr::exists([null], 1)); @@ -900,14 +900,6 @@ public function testSet(): void $this->assertEquals([1 => 'hAz'], Arr::set($array, 1, 'hAz')); } - public function testShuffleWithSeed(): void - { - $this->assertEquals( - Arr::shuffle(range(0, 100, 10), 1234), - Arr::shuffle(range(0, 100, 10), 1234) - ); - } - public function testSort(): void { $unsorted = [ diff --git a/tests/Support/SupportCollectionTest.php b/tests/Support/SupportCollectionTest.php index 3100ce36..6dd31818 100755 --- a/tests/Support/SupportCollectionTest.php +++ b/tests/Support/SupportCollectionTest.php @@ -263,11 +263,11 @@ public function testChunk (): void } - public function testListsWithArrayAndObjectValues(): void + public function testPluckWithArrayAndObjectValues(): void { $data = new Collection([(object) ['name' => 'taylor', 'email' => 'foo'], ['name' => 'dayle', 'email' => 'bar']]); - $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->lists('email', 'name')); - $this->assertEquals(['foo', 'bar'], $data->lists('email')); + $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->pluck('email', 'name')->all()); + $this->assertEquals(['foo', 'bar'], $data->pluck('email')->all()); } @@ -340,13 +340,13 @@ public function testSplice(): void } - public function testGetListValueWithAccessors(): void + public function testGetPluckValueWithAccessors(): void { $model = new TestAccessorEloquentTestStub(['some' => 'foo']); $modelTwo = new TestAccessorEloquentTestStub(['some' => 'bar']); $data = new Collection([$model, $modelTwo]); - $this->assertEquals(['foo', 'bar'], $data->lists('some')); + $this->assertEquals(['foo', 'bar'], $data->pluck('some')->all()); } @@ -440,7 +440,7 @@ public function testValueRetrieverAcceptsDotNotation(): void ]); $c = $c->sortBy('foo.bar'); - $this->assertEquals([2, 1], $c->lists('id')); + $this->assertEquals([2, 1], $c->pluck('id')->all()); } @@ -516,6 +516,19 @@ public function __get($attribute) } + public function __isset($attribute) + { + $accessor = 'get'.lcfirst((string) $attribute).'Attribute'; + + if (method_exists($this, $accessor)) + { + return ! is_null($this->$accessor()); + } + + return isset($this->$attribute); + } + + public function getSomeAttribute() { return $this->attributes['some']; From fca00e6218672d0b3fef6ea570962c4a7e7f9a4f Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 10:15:06 +0700 Subject: [PATCH 04/35] feat(support): swap Reflector for illuminate/reflection:^13 (task 4.1d) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last copy-source seed from MIGRATION-SPLIT-MAP §3a. Fork's 947-byte src/Illuminate/Support/Reflector.php stub (a single getParameterClassName, itself copied from Container\Util) is shadowed by the real package, which is a strict superset (getClassAttribute(s), getParameterClassNames, isCallable, isParameterSubclassOf, isParameterBackedEnumWithStringBackingType). The package's helpers.php defines lazy()/proxy(); no overlap with the fork's globals, and every function is function_exists-guarded on both sides. Suite 1646 green / 3658 assertions. Seeds complete (Contracts, Macroable, Conditionable, Collections, Reflection). Next is §1: the eleven-member SCC-1 window. --- composer.json | 1 + src/Illuminate/Support/Reflector.php | 40 ---------------------------- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 src/Illuminate/Support/Reflector.php diff --git a/composer.json b/composer.json index 70497caf..84459593 100755 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "illuminate/conditionable": "^13", "illuminate/contracts": "^13", "illuminate/macroable": "^13", + "illuminate/reflection": "^13", "ircmaxell/password-compat": "~1.0", "laravel/serializable-closure": "^1.2", "monolog/monolog": "^3.10", diff --git a/src/Illuminate/Support/Reflector.php b/src/Illuminate/Support/Reflector.php deleted file mode 100644 index e96f41ed..00000000 --- a/src/Illuminate/Support/Reflector.php +++ /dev/null @@ -1,40 +0,0 @@ -getType(); - - if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) { - return null; - } - - $name = $type->getName(); - - if (! is_null($class = $parameter->getDeclaringClass())) { - if ($name === 'self') { - return $class->getName(); - } - - if ($name === 'parent' && $parent = $class->getParentClass()) { - return $parent->getName(); - } - } - - return $name; - } -} \ No newline at end of file From 6942197228c7d8d1db56cf956f8e50c07e69b3b5 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 16:09:35 +0700 Subject: [PATCH 05/35] refactor(container): sweep bindShared() callers to singleton() (task 4.1, SCC-1 container-prep) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepares the SCC-1 atomic window: v13's illuminate/container has singleton() but no bindShared()/share(). Migrate all 84 fork callers (32 service providers) from ->bindShared() to ->singleton() while the fork container still ships both. Semantics: fork bindShared($a,$c) = bind($a, share($c), true); singleton($a,$c) = bind($a,$c,true). The share() wrapper is a redundant static-var memoization on top of the shared-instance cache — dropping it converges to v13 behavior. Fork suite green (1646, 0 fail). bindShared()/share() methods are KEPT in the container (app service providers still call them until the app matched-pair conform); they go with the container swap in the atomic window. The Foundation\Application BindingResolutionException import redirect is also deferred to the window (fork concrete extends \Exception, not the Contracts type). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Auth/AuthServiceProvider.php | 2 +- .../Auth/Reminders/ReminderServiceProvider.php | 10 +++++----- src/Illuminate/Cache/CacheServiceProvider.php | 10 +++++----- .../Cookie/CookieServiceProvider.php | 2 +- .../Database/DatabaseServiceProvider.php | 4 ++-- .../Database/MigrationServiceProvider.php | 18 +++++++++--------- .../Database/SeedServiceProvider.php | 4 ++-- .../Encryption/EncryptionServiceProvider.php | 2 +- .../Providers/ArtisanServiceProvider.php | 6 +++--- .../CommandCreatorServiceProvider.php | 2 +- .../Providers/ComposerServiceProvider.php | 4 ++-- .../Providers/KeyGeneratorServiceProvider.php | 2 +- .../Providers/MaintenanceServiceProvider.php | 4 ++-- .../Providers/OptimizeServiceProvider.php | 4 ++-- .../Providers/PublisherServiceProvider.php | 16 ++++++++-------- .../Providers/RouteListServiceProvider.php | 2 +- .../Providers/ServerServiceProvider.php | 2 +- .../Providers/TinkerServiceProvider.php | 2 +- src/Illuminate/Hashing/HashServiceProvider.php | 2 +- src/Illuminate/Html/HtmlServiceProvider.php | 4 ++-- src/Illuminate/Mail/MailServiceProvider.php | 6 +++--- .../Pagination/PaginationServiceProvider.php | 2 +- .../Queue/FailConsoleServiceProvider.php | 10 +++++----- src/Illuminate/Queue/QueueServiceProvider.php | 18 +++++++++--------- src/Illuminate/Redis/RedisServiceProvider.php | 2 +- .../Routing/ControllerServiceProvider.php | 2 +- .../Session/CommandsServiceProvider.php | 2 +- .../Session/SessionServiceProvider.php | 4 ++-- .../Translation/TranslationServiceProvider.php | 4 ++-- .../Validation/ValidationServiceProvider.php | 4 ++-- src/Illuminate/View/ViewServiceProvider.php | 8 ++++---- .../Workbench/WorkbenchServiceProvider.php | 4 ++-- 32 files changed, 84 insertions(+), 84 deletions(-) diff --git a/src/Illuminate/Auth/AuthServiceProvider.php b/src/Illuminate/Auth/AuthServiceProvider.php index 9a6bd7f0..4a5c509d 100755 --- a/src/Illuminate/Auth/AuthServiceProvider.php +++ b/src/Illuminate/Auth/AuthServiceProvider.php @@ -18,7 +18,7 @@ class AuthServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('auth', function($app) + $this->app->singleton('auth', function($app) { // Once the authentication service has actually been requested by the developer // we will set a variable in the application indicating such. This helps us diff --git a/src/Illuminate/Auth/Reminders/ReminderServiceProvider.php b/src/Illuminate/Auth/Reminders/ReminderServiceProvider.php index 0c366402..125c0bd1 100755 --- a/src/Illuminate/Auth/Reminders/ReminderServiceProvider.php +++ b/src/Illuminate/Auth/Reminders/ReminderServiceProvider.php @@ -36,7 +36,7 @@ public function register() */ protected function registerPasswordBroker() { - $this->app->bindShared('auth.reminder', function($app) + $this->app->singleton('auth.reminder', function($app) { // The reminder repository is responsible for storing the user e-mail addresses // and password reset tokens. It will be used to verify the tokens are valid @@ -65,7 +65,7 @@ protected function registerPasswordBroker() */ protected function registerReminderRepository() { - $this->app->bindShared('auth.reminder.repository', function($app) + $this->app->singleton('auth.reminder.repository', function($app) { $connection = $app['db']->connection(); @@ -89,17 +89,17 @@ protected function registerReminderRepository() */ protected function registerCommands() { - $this->app->bindShared('command.auth.reminders', function($app) + $this->app->singleton('command.auth.reminders', function($app) { return new RemindersTableCommand($app['files']); }); - $this->app->bindShared('command.auth.reminders.clear', function() + $this->app->singleton('command.auth.reminders.clear', function() { return new ClearRemindersCommand; }); - $this->app->bindShared('command.auth.reminders.controller', function($app) + $this->app->singleton('command.auth.reminders.controller', function($app) { return new RemindersControllerCommand($app['files']); }); diff --git a/src/Illuminate/Cache/CacheServiceProvider.php b/src/Illuminate/Cache/CacheServiceProvider.php index 4c17e24c..0e3c611c 100755 --- a/src/Illuminate/Cache/CacheServiceProvider.php +++ b/src/Illuminate/Cache/CacheServiceProvider.php @@ -18,17 +18,17 @@ class CacheServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('cache', function($app) + $this->app->singleton('cache', function($app) { return new CacheManager($app); }); - $this->app->bindShared('cache.store', function($app) + $this->app->singleton('cache.store', function($app) { return $app['cache']->driver(); }); - $this->app->bindShared('memcached.connector', function() + $this->app->singleton('memcached.connector', function() { return new MemcachedConnector; }); @@ -43,12 +43,12 @@ public function register() */ public function registerCommands() { - $this->app->bindShared('command.cache.clear', function($app) + $this->app->singleton('command.cache.clear', function($app) { return new Console\ClearCommand($app['cache'], $app['files']); }); - $this->app->bindShared('command.cache.table', function($app) + $this->app->singleton('command.cache.table', function($app) { return new Console\CacheTableCommand($app['files']); }); diff --git a/src/Illuminate/Cookie/CookieServiceProvider.php b/src/Illuminate/Cookie/CookieServiceProvider.php index 1c7b9d05..f463302e 100755 --- a/src/Illuminate/Cookie/CookieServiceProvider.php +++ b/src/Illuminate/Cookie/CookieServiceProvider.php @@ -11,7 +11,7 @@ class CookieServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('cookie', function($app) + $this->app->singleton('cookie', function($app) { $config = $app['config']['session']; diff --git a/src/Illuminate/Database/DatabaseServiceProvider.php b/src/Illuminate/Database/DatabaseServiceProvider.php index 49264222..32005da5 100755 --- a/src/Illuminate/Database/DatabaseServiceProvider.php +++ b/src/Illuminate/Database/DatabaseServiceProvider.php @@ -28,7 +28,7 @@ public function register() // The connection factory is used to create the actual connection instances on // the database. We will inject the factory into the manager so that it may // make the connections while they are actually needed and not of before. - $this->app->bindShared('db.factory', function($app) + $this->app->singleton('db.factory', function($app) { return new ConnectionFactory($app); }); @@ -36,7 +36,7 @@ public function register() // The database manager is used to resolve various connections, since multiple // connections might be managed. It also implements the connection resolver // interface which may be used by other components requiring connections. - $this->app->bindShared('db', function($app) + $this->app->singleton('db', function($app) { return new DatabaseManager($app, $app['db.factory']); }); diff --git a/src/Illuminate/Database/MigrationServiceProvider.php b/src/Illuminate/Database/MigrationServiceProvider.php index 4bf1d2e7..58782c86 100755 --- a/src/Illuminate/Database/MigrationServiceProvider.php +++ b/src/Illuminate/Database/MigrationServiceProvider.php @@ -44,7 +44,7 @@ public function register() */ protected function registerRepository() { - $this->app->bindShared('migration.repository', function($app) + $this->app->singleton('migration.repository', function($app) { $table = $app['config']['database.migrations']; @@ -62,7 +62,7 @@ protected function registerMigrator() // The migrator is responsible for actually running and rollback the migration // files in the application. We'll pass in our database connection resolver // so the migrator can resolve any of these connections when it needs to. - $this->app->bindShared('migrator', function($app) + $this->app->singleton('migrator', function($app) { $repository = $app['migration.repository']; @@ -104,7 +104,7 @@ protected function registerCommands() */ protected function registerMigrateCommand() { - $this->app->bindShared('command.migrate', function($app) + $this->app->singleton('command.migrate', function($app) { $packagePath = $app['path.base'].'/vendor'; @@ -119,7 +119,7 @@ protected function registerMigrateCommand() */ protected function registerRollbackCommand() { - $this->app->bindShared('command.migrate.rollback', function($app) + $this->app->singleton('command.migrate.rollback', function($app) { return new RollbackCommand($app['migrator']); }); @@ -132,7 +132,7 @@ protected function registerRollbackCommand() */ protected function registerResetCommand() { - $this->app->bindShared('command.migrate.reset', function($app) + $this->app->singleton('command.migrate.reset', function($app) { return new ResetCommand($app['migrator']); }); @@ -145,7 +145,7 @@ protected function registerResetCommand() */ protected function registerRefreshCommand() { - $this->app->bindShared('command.migrate.refresh', function() + $this->app->singleton('command.migrate.refresh', function() { return new RefreshCommand; }); @@ -158,7 +158,7 @@ protected function registerRefreshCommand() */ protected function registerInstallCommand() { - $this->app->bindShared('command.migrate.install', function($app) + $this->app->singleton('command.migrate.install', function($app) { return new InstallCommand($app['migration.repository']); }); @@ -173,7 +173,7 @@ protected function registerMakeCommand() { $this->registerCreator(); - $this->app->bindShared('command.migrate.make', function($app) + $this->app->singleton('command.migrate.make', function($app) { // Once we have the migration creator registered, we will create the command // and inject the creator. The creator is responsible for the actual file @@ -193,7 +193,7 @@ protected function registerMakeCommand() */ protected function registerCreator() { - $this->app->bindShared('migration.creator', function($app) + $this->app->singleton('migration.creator', function($app) { return new MigrationCreator($app['files']); }); diff --git a/src/Illuminate/Database/SeedServiceProvider.php b/src/Illuminate/Database/SeedServiceProvider.php index 00203aa1..886518ec 100755 --- a/src/Illuminate/Database/SeedServiceProvider.php +++ b/src/Illuminate/Database/SeedServiceProvider.php @@ -21,7 +21,7 @@ public function register() { $this->registerSeedCommand(); - $this->app->bindShared('seeder', function() + $this->app->singleton('seeder', function() { return new Seeder; }); @@ -36,7 +36,7 @@ public function register() */ protected function registerSeedCommand() { - $this->app->bindShared('command.seed', function($app) + $this->app->singleton('command.seed', function($app) { return new SeedCommand($app['db']); }); diff --git a/src/Illuminate/Encryption/EncryptionServiceProvider.php b/src/Illuminate/Encryption/EncryptionServiceProvider.php index c6329d19..507192e0 100755 --- a/src/Illuminate/Encryption/EncryptionServiceProvider.php +++ b/src/Illuminate/Encryption/EncryptionServiceProvider.php @@ -11,7 +11,7 @@ class EncryptionServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('encrypter', function($app) + $this->app->singleton('encrypter', function($app) { return new Encrypter($app['config']['app.key']); }); diff --git a/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php b/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php index 916175c9..659e812d 100755 --- a/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php @@ -21,17 +21,17 @@ class ArtisanServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('artisan', function($app) + $this->app->singleton('artisan', function($app) { return new Artisan($app); }); - $this->app->bindShared('command.changes', function() + $this->app->singleton('command.changes', function() { return new ChangesCommand; }); - $this->app->bindShared('command.environment', function() + $this->app->singleton('command.environment', function() { return new EnvironmentCommand; }); diff --git a/src/Illuminate/Foundation/Providers/CommandCreatorServiceProvider.php b/src/Illuminate/Foundation/Providers/CommandCreatorServiceProvider.php index 56092194..3679effe 100755 --- a/src/Illuminate/Foundation/Providers/CommandCreatorServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/CommandCreatorServiceProvider.php @@ -19,7 +19,7 @@ class CommandCreatorServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('command.command.make', function($app) + $this->app->singleton('command.command.make', function($app) { return new CommandMakeCommand($app['files']); }); diff --git a/src/Illuminate/Foundation/Providers/ComposerServiceProvider.php b/src/Illuminate/Foundation/Providers/ComposerServiceProvider.php index 2022d7f5..e1b421ef 100755 --- a/src/Illuminate/Foundation/Providers/ComposerServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/ComposerServiceProvider.php @@ -20,12 +20,12 @@ class ComposerServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('composer', function($app) + $this->app->singleton('composer', function($app) { return new Composer($app['files'], $app['path.base']); }); - $this->app->bindShared('command.dump-autoload', function($app) + $this->app->singleton('command.dump-autoload', function($app) { return new AutoloadCommand($app['composer']); }); diff --git a/src/Illuminate/Foundation/Providers/KeyGeneratorServiceProvider.php b/src/Illuminate/Foundation/Providers/KeyGeneratorServiceProvider.php index f73f9305..197077dd 100755 --- a/src/Illuminate/Foundation/Providers/KeyGeneratorServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/KeyGeneratorServiceProvider.php @@ -19,7 +19,7 @@ class KeyGeneratorServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('command.key.generate', function($app) + $this->app->singleton('command.key.generate', function($app) { return new KeyGenerateCommand($app['files']); }); diff --git a/src/Illuminate/Foundation/Providers/MaintenanceServiceProvider.php b/src/Illuminate/Foundation/Providers/MaintenanceServiceProvider.php index 703f6d3c..6de7a4bd 100755 --- a/src/Illuminate/Foundation/Providers/MaintenanceServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/MaintenanceServiceProvider.php @@ -20,12 +20,12 @@ class MaintenanceServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('command.up', function() + $this->app->singleton('command.up', function() { return new UpCommand; }); - $this->app->bindShared('command.down', function() + $this->app->singleton('command.down', function() { return new DownCommand; }); diff --git a/src/Illuminate/Foundation/Providers/OptimizeServiceProvider.php b/src/Illuminate/Foundation/Providers/OptimizeServiceProvider.php index cc809bc5..f780fa85 100755 --- a/src/Illuminate/Foundation/Providers/OptimizeServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/OptimizeServiceProvider.php @@ -34,7 +34,7 @@ public function register() */ protected function registerOptimizeCommand() { - $this->app->bindShared('command.optimize', function($app) + $this->app->singleton('command.optimize', function($app) { return new OptimizeCommand($app['composer']); }); @@ -47,7 +47,7 @@ protected function registerOptimizeCommand() */ protected function registerClearCompiledCommand() { - $this->app->bindShared('command.clear-compiled', function() + $this->app->singleton('command.clear-compiled', function() { return new ClearCompiledCommand; }); diff --git a/src/Illuminate/Foundation/Providers/PublisherServiceProvider.php b/src/Illuminate/Foundation/Providers/PublisherServiceProvider.php index ee324c9c..085b83a9 100755 --- a/src/Illuminate/Foundation/Providers/PublisherServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/PublisherServiceProvider.php @@ -49,7 +49,7 @@ protected function registerAssetPublisher() { $this->registerAssetPublishCommand(); - $this->app->bindShared('asset.publisher', function($app) + $this->app->singleton('asset.publisher', function($app) { $publicPath = $app['path.public']; @@ -71,7 +71,7 @@ protected function registerAssetPublisher() */ protected function registerAssetPublishCommand() { - $this->app->bindShared('command.asset.publish', function($app) + $this->app->singleton('command.asset.publish', function($app) { return new AssetPublishCommand($app['asset.publisher']); }); @@ -86,7 +86,7 @@ protected function registerConfigPublisher() { $this->registerConfigPublishCommand(); - $this->app->bindShared('config.publisher', function($app) + $this->app->singleton('config.publisher', function($app) { $path = $app['path'].'/config'; @@ -108,7 +108,7 @@ protected function registerConfigPublisher() */ protected function registerConfigPublishCommand() { - $this->app->bindShared('command.config.publish', function($app) + $this->app->singleton('command.config.publish', function($app) { return new ConfigPublishCommand($app['config.publisher']); }); @@ -123,7 +123,7 @@ protected function registerViewPublisher() { $this->registerViewPublishCommand(); - $this->app->bindShared('view.publisher', function($app) + $this->app->singleton('view.publisher', function($app) { $viewPath = $app['path'].'/views'; @@ -145,7 +145,7 @@ protected function registerViewPublisher() */ protected function registerViewPublishCommand() { - $this->app->bindShared('command.view.publish', function($app) + $this->app->singleton('command.view.publish', function($app) { return new ViewPublishCommand($app['view.publisher']); }); @@ -160,7 +160,7 @@ protected function registerMigrationPublisher() { $this->registerMigratePublishCommand(); - $this->app->bindShared('migration.publisher', function($app) + $this->app->singleton('migration.publisher', function($app) { return new MigrationPublisher($app['files']); }); @@ -173,7 +173,7 @@ protected function registerMigrationPublisher() */ protected function registerMigratePublishCommand() { - $this->app->bindShared('command.migrate.publish', function() + $this->app->singleton('command.migrate.publish', function() { return new MigratePublishCommand; }); diff --git a/src/Illuminate/Foundation/Providers/RouteListServiceProvider.php b/src/Illuminate/Foundation/Providers/RouteListServiceProvider.php index af1e9bae..02179b54 100755 --- a/src/Illuminate/Foundation/Providers/RouteListServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/RouteListServiceProvider.php @@ -19,7 +19,7 @@ class RouteListServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('command.routes', function($app) + $this->app->singleton('command.routes', function($app) { return new RoutesCommand($app['router']); }); diff --git a/src/Illuminate/Foundation/Providers/ServerServiceProvider.php b/src/Illuminate/Foundation/Providers/ServerServiceProvider.php index f6af6197..3e3762b9 100755 --- a/src/Illuminate/Foundation/Providers/ServerServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/ServerServiceProvider.php @@ -19,7 +19,7 @@ class ServerServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('command.serve', function() + $this->app->singleton('command.serve', function() { return new ServeCommand; }); diff --git a/src/Illuminate/Foundation/Providers/TinkerServiceProvider.php b/src/Illuminate/Foundation/Providers/TinkerServiceProvider.php index 55a4a230..502bbf1c 100755 --- a/src/Illuminate/Foundation/Providers/TinkerServiceProvider.php +++ b/src/Illuminate/Foundation/Providers/TinkerServiceProvider.php @@ -19,7 +19,7 @@ class TinkerServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('command.tinker', function() + $this->app->singleton('command.tinker', function() { return new TinkerCommand; }); diff --git a/src/Illuminate/Hashing/HashServiceProvider.php b/src/Illuminate/Hashing/HashServiceProvider.php index 59af2927..486bf6e4 100755 --- a/src/Illuminate/Hashing/HashServiceProvider.php +++ b/src/Illuminate/Hashing/HashServiceProvider.php @@ -18,7 +18,7 @@ class HashServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('hash', function() { return new BcryptHasher; }); + $this->app->singleton('hash', function() { return new BcryptHasher; }); } /** diff --git a/src/Illuminate/Html/HtmlServiceProvider.php b/src/Illuminate/Html/HtmlServiceProvider.php index 8cec79ad..0c1f2413 100755 --- a/src/Illuminate/Html/HtmlServiceProvider.php +++ b/src/Illuminate/Html/HtmlServiceProvider.php @@ -30,7 +30,7 @@ public function register() */ protected function registerHtmlBuilder() { - $this->app->bindShared('html', function($app) + $this->app->singleton('html', function($app) { return new HtmlBuilder($app['url']); }); @@ -43,7 +43,7 @@ protected function registerHtmlBuilder() */ protected function registerFormBuilder() { - $this->app->bindShared('form', function($app) + $this->app->singleton('form', function($app) { $form = new FormBuilder($app['html'], $app['url'], $app['session.store']->getToken()); diff --git a/src/Illuminate/Mail/MailServiceProvider.php b/src/Illuminate/Mail/MailServiceProvider.php index 47053d38..4728a1d0 100755 --- a/src/Illuminate/Mail/MailServiceProvider.php +++ b/src/Illuminate/Mail/MailServiceProvider.php @@ -27,7 +27,7 @@ public function register(): void { $me = $this; - $this->app->bindShared('mailer', function($app) use ($me) + $this->app->singleton('mailer', function($app) use ($me) { $me->registerSymfonyMailer(); @@ -190,7 +190,7 @@ protected function registerMailTransport(array $config): void */ // protected function registerMailgunTransport(array $config): void // { -// $this->app->bindShared('symfony.transport', function() use ($config) +// $this->app->singleton('symfony.transport', function() use ($config) // { // $factory = new MailgunTransportFactory(null, $this->getHttpClient($config)); // @@ -215,7 +215,7 @@ protected function registerMailTransport(array $config): void */ protected function registerLogTransport(array $config): void { - $this->app->bindShared('symfony.transport', fn($app) => new LogTransport($app->make('Psr\Log\LoggerInterface'))); + $this->app->singleton('symfony.transport', fn($app) => new LogTransport($app->make('Psr\Log\LoggerInterface'))); } // /** diff --git a/src/Illuminate/Pagination/PaginationServiceProvider.php b/src/Illuminate/Pagination/PaginationServiceProvider.php index 955ffb15..933e82ca 100755 --- a/src/Illuminate/Pagination/PaginationServiceProvider.php +++ b/src/Illuminate/Pagination/PaginationServiceProvider.php @@ -18,7 +18,7 @@ class PaginationServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('paginator', function($app) + $this->app->singleton('paginator', function($app) { $paginator = new Factory($app['request'], $app['view'], $app['translator']); diff --git a/src/Illuminate/Queue/FailConsoleServiceProvider.php b/src/Illuminate/Queue/FailConsoleServiceProvider.php index c510a0bc..d2eeaf50 100644 --- a/src/Illuminate/Queue/FailConsoleServiceProvider.php +++ b/src/Illuminate/Queue/FailConsoleServiceProvider.php @@ -23,27 +23,27 @@ class FailConsoleServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('command.queue.failed', function() + $this->app->singleton('command.queue.failed', function() { return new ListFailedCommand; }); - $this->app->bindShared('command.queue.retry', function() + $this->app->singleton('command.queue.retry', function() { return new RetryCommand; }); - $this->app->bindShared('command.queue.forget', function() + $this->app->singleton('command.queue.forget', function() { return new ForgetFailedCommand; }); - $this->app->bindShared('command.queue.flush', function() + $this->app->singleton('command.queue.flush', function() { return new FlushFailedCommand; }); - $this->app->bindShared('command.queue.failed-table', function($app) + $this->app->singleton('command.queue.failed-table', function($app) { return new FailedTableCommand($app['files']); }); diff --git a/src/Illuminate/Queue/QueueServiceProvider.php b/src/Illuminate/Queue/QueueServiceProvider.php index f6829f25..26169c89 100755 --- a/src/Illuminate/Queue/QueueServiceProvider.php +++ b/src/Illuminate/Queue/QueueServiceProvider.php @@ -49,7 +49,7 @@ public function register() */ protected function registerManager() { - $this->app->bindShared('queue', function($app) + $this->app->singleton('queue', function($app) { // Once we have an instance of the queue manager, we will register the various // resolvers for the queue connectors. These connectors are responsible for @@ -73,7 +73,7 @@ protected function registerWorker() $this->registerRestartCommand(); - $this->app->bindShared('queue.worker', function($app) + $this->app->singleton('queue.worker', function($app) { return new Worker($app['queue'], $app['queue.failer'], $app['events']); }); @@ -86,7 +86,7 @@ protected function registerWorker() */ protected function registerWorkCommand() { - $this->app->bindShared('command.queue.work', function($app) + $this->app->singleton('command.queue.work', function($app) { return new WorkCommand($app['queue.worker']); }); @@ -103,7 +103,7 @@ protected function registerListener() { $this->registerListenCommand(); - $this->app->bindShared('queue.listener', function($app) + $this->app->singleton('queue.listener', function($app) { return new Listener($app['path.base']); }); @@ -116,7 +116,7 @@ protected function registerListener() */ protected function registerListenCommand() { - $this->app->bindShared('command.queue.listen', function($app) + $this->app->singleton('command.queue.listen', function($app) { return new ListenCommand($app['queue.listener']); }); @@ -131,7 +131,7 @@ protected function registerListenCommand() */ public function registerRestartCommand() { - $this->app->bindShared('command.queue.restart', function() + $this->app->singleton('command.queue.restart', function() { return new RestartCommand; }); @@ -146,7 +146,7 @@ public function registerRestartCommand() */ protected function registerSubscriber() { - $this->app->bindShared('command.queue.subscribe', function() + $this->app->singleton('command.queue.subscribe', function() { return new SubscribeCommand; }); @@ -267,7 +267,7 @@ protected function registerIronRequestBinder() */ protected function registerFailedJobServices() { - $this->app->bindShared('queue.failer', function($app) + $this->app->singleton('queue.failer', function($app) { $config = $app['config']['queue.failed']; @@ -282,7 +282,7 @@ protected function registerFailedJobServices() */ protected function registerQueueClosure() { - $this->app->bindShared('IlluminateQueueClosure', function($app) + $this->app->singleton('IlluminateQueueClosure', function($app) { return new IlluminateQueueClosure($app['encrypter']); }); diff --git a/src/Illuminate/Redis/RedisServiceProvider.php b/src/Illuminate/Redis/RedisServiceProvider.php index ae2fe9e8..39236a98 100755 --- a/src/Illuminate/Redis/RedisServiceProvider.php +++ b/src/Illuminate/Redis/RedisServiceProvider.php @@ -18,7 +18,7 @@ class RedisServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('redis', function($app) + $this->app->singleton('redis', function($app) { return new Database($app['config']['database.redis']); }); diff --git a/src/Illuminate/Routing/ControllerServiceProvider.php b/src/Illuminate/Routing/ControllerServiceProvider.php index ba12f250..0df75c99 100644 --- a/src/Illuminate/Routing/ControllerServiceProvider.php +++ b/src/Illuminate/Routing/ControllerServiceProvider.php @@ -32,7 +32,7 @@ public function register() */ protected function registerGenerator() { - $this->app->bindShared('command.controller.make', function($app) + $this->app->singleton('command.controller.make', function($app) { // The controller generator is responsible for building resourceful controllers // quickly and easily for the developers via the Artisan CLI. We'll go ahead diff --git a/src/Illuminate/Session/CommandsServiceProvider.php b/src/Illuminate/Session/CommandsServiceProvider.php index 5f7b01c7..81ef0bae 100755 --- a/src/Illuminate/Session/CommandsServiceProvider.php +++ b/src/Illuminate/Session/CommandsServiceProvider.php @@ -18,7 +18,7 @@ class CommandsServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('command.session.database', function($app) + $this->app->singleton('command.session.database', function($app) { return new Console\SessionTableCommand($app['files']); }); diff --git a/src/Illuminate/Session/SessionServiceProvider.php b/src/Illuminate/Session/SessionServiceProvider.php index 0efe172d..70fd1521 100755 --- a/src/Illuminate/Session/SessionServiceProvider.php +++ b/src/Illuminate/Session/SessionServiceProvider.php @@ -38,7 +38,7 @@ protected function setupDefaultDriver() */ protected function registerSessionManager() { - $this->app->bindShared('session', function($app) + $this->app->singleton('session', function($app) { return new SessionManager($app); }); @@ -51,7 +51,7 @@ protected function registerSessionManager() */ protected function registerSessionDriver() { - $this->app->bindShared('session.store', function($app) + $this->app->singleton('session.store', function($app) { // First, we will create the session manager which is responsible for the // creation of the various session drivers when they are needed by the diff --git a/src/Illuminate/Translation/TranslationServiceProvider.php b/src/Illuminate/Translation/TranslationServiceProvider.php index 4b696501..e508e2eb 100755 --- a/src/Illuminate/Translation/TranslationServiceProvider.php +++ b/src/Illuminate/Translation/TranslationServiceProvider.php @@ -20,7 +20,7 @@ public function register() { $this->registerLoader(); - $this->app->bindShared('translator', function($app) + $this->app->singleton('translator', function($app) { $loader = $app['translation.loader']; @@ -44,7 +44,7 @@ public function register() */ protected function registerLoader() { - $this->app->bindShared('translation.loader', function($app) + $this->app->singleton('translation.loader', function($app) { return new FileLoader($app['files'], $app['path'].'/lang'); }); diff --git a/src/Illuminate/Validation/ValidationServiceProvider.php b/src/Illuminate/Validation/ValidationServiceProvider.php index b6b2dbdd..3c1ab1b7 100755 --- a/src/Illuminate/Validation/ValidationServiceProvider.php +++ b/src/Illuminate/Validation/ValidationServiceProvider.php @@ -20,7 +20,7 @@ public function register() { $this->registerPresenceVerifier(); - $this->app->bindShared('validator', function($app) + $this->app->singleton('validator', function($app) { $validator = new Factory($app['translator'], $app); @@ -43,7 +43,7 @@ public function register() */ protected function registerPresenceVerifier() { - $this->app->bindShared('validation.presence', function($app) + $this->app->singleton('validation.presence', function($app) { return new DatabasePresenceVerifier($app['db']); }); diff --git a/src/Illuminate/View/ViewServiceProvider.php b/src/Illuminate/View/ViewServiceProvider.php index e186da87..7763b869 100755 --- a/src/Illuminate/View/ViewServiceProvider.php +++ b/src/Illuminate/View/ViewServiceProvider.php @@ -35,7 +35,7 @@ public function register() */ public function registerEngineResolver() { - $this->app->bindShared('view.engine.resolver', function() + $this->app->singleton('view.engine.resolver', function() { $resolver = new EngineResolver; @@ -75,7 +75,7 @@ public function registerBladeEngine($resolver) // The Compiler engine requires an instance of the CompilerInterface, which in // this case will be the Blade compiler, so we'll first create the compiler // instance to pass into the engine so it can compile the views properly. - $app->bindShared('blade.compiler', function($app) + $app->singleton('blade.compiler', function($app) { $cache = $app['path.storage'].'/views'; @@ -95,7 +95,7 @@ public function registerBladeEngine($resolver) */ public function registerViewFinder() { - $this->app->bindShared('view.finder', function($app) + $this->app->singleton('view.finder', function($app) { $paths = $app['config']['view.paths']; @@ -110,7 +110,7 @@ public function registerViewFinder() */ public function registerFactory() { - $this->app->bindShared('view', function($app) + $this->app->singleton('view', function($app) { // Next we need to grab the engine resolver instance that will be used by the // environment. The resolver will be used by an environment to get each of diff --git a/src/Illuminate/Workbench/WorkbenchServiceProvider.php b/src/Illuminate/Workbench/WorkbenchServiceProvider.php index 4dcd4e25..d2e41e82 100755 --- a/src/Illuminate/Workbench/WorkbenchServiceProvider.php +++ b/src/Illuminate/Workbench/WorkbenchServiceProvider.php @@ -19,12 +19,12 @@ class WorkbenchServiceProvider extends ServiceProvider { */ public function register() { - $this->app->bindShared('package.creator', function($app) + $this->app->singleton('package.creator', function($app) { return new PackageCreator($app['files']); }); - $this->app->bindShared('command.workbench', function($app) + $this->app->singleton('command.workbench', function($app) { return new WorkbenchMakeCommand($app['package.creator']); }); From f1fff797ebb444b6c2e8f8cbc029225ffc6ebf96 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 16:19:22 +0700 Subject: [PATCH 06/35] refactor(container): convert $app->share() bindings to singleton() (task 4.1, SCC-1 container-prep) Second prep slice for the container swap: v13 illuminate/container has no share(). The 15 legacy `$this->app['key'] = $this->app->share($closure)` bindings (Events, Routing x4, CachedRouting, Exception x6, Mail x3) are the L4.0 idiom where share() memoizes a closure via a static var and offsetSet binds it non-shared. In v13 `$app['key'] = $closure` binds NON-shared, so the singleton must be explicit: rewritten to `$this->app->singleton('key', $closure)`. View Factory's own share() (view-data sharing, different API) is untouched. Fork suite green (1646, 0 fail). Missed in the bindShared sweep because an rtk-proxied grep wrongly reported zero ->share( callsites; re-verified via `rtk proxy grep`. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../CachedRouting/RoutingServiceProvider.php | 2 +- src/Illuminate/Events/EventServiceProvider.php | 2 +- .../Exception/ExceptionServiceProvider.php | 12 ++++++------ src/Illuminate/Mail/MailServiceProvider.php | 6 +++--- src/Illuminate/Routing/RoutingServiceProvider.php | 8 ++++---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Illuminate/CachedRouting/RoutingServiceProvider.php b/src/Illuminate/CachedRouting/RoutingServiceProvider.php index eff880aa..472777e6 100644 --- a/src/Illuminate/CachedRouting/RoutingServiceProvider.php +++ b/src/Illuminate/CachedRouting/RoutingServiceProvider.php @@ -56,7 +56,7 @@ public function register() */ protected function registerRouter() { - $this->app['router'] = $this->app->share( + $this->app->singleton('router', function ($app) { $router = new Router($app['events'], $app); diff --git a/src/Illuminate/Events/EventServiceProvider.php b/src/Illuminate/Events/EventServiceProvider.php index 94b8e4be..e78ac72e 100755 --- a/src/Illuminate/Events/EventServiceProvider.php +++ b/src/Illuminate/Events/EventServiceProvider.php @@ -11,7 +11,7 @@ class EventServiceProvider extends ServiceProvider { */ public function register() { - $this->app['events'] = $this->app->share(function($app) + $this->app->singleton('events', function($app) { return new Dispatcher($app); }); diff --git a/src/Illuminate/Exception/ExceptionServiceProvider.php b/src/Illuminate/Exception/ExceptionServiceProvider.php index c2fa6a68..0d65df23 100755 --- a/src/Illuminate/Exception/ExceptionServiceProvider.php +++ b/src/Illuminate/Exception/ExceptionServiceProvider.php @@ -38,7 +38,7 @@ protected function registerDisplayers() */ protected function registerHandler() { - $this->app['exception'] = $this->app->share(function($app) + $this->app->singleton('exception', function($app) { return new Handler($app, $app['exception.plain'], $app['exception.debug']); }); @@ -51,7 +51,7 @@ protected function registerHandler() */ protected function registerPlainDisplayer() { - $this->app['exception.plain'] = $this->app->share(function($app) + $this->app->singleton('exception.plain', function($app) { // If the application is running in a console environment, we will just always // use the debug handler as there is no point in the console ever returning @@ -76,7 +76,7 @@ protected function registerDebugDisplayer() { $this->registerWhoops(); - $this->app['exception.debug'] = $this->app->share(function($app) + $this->app->singleton('exception.debug', function($app) { return new WhoopsDisplayer($app['whoops'], $app->runningInConsole()); }); @@ -91,7 +91,7 @@ protected function registerWhoops() { $this->registerWhoopsHandler(); - $this->app['whoops'] = $this->app->share(function($app) + $this->app->singleton('whoops', function($app) { // We will instruct Whoops to not exit after it displays the exception as it // will otherwise run out before we can do anything else. We just want to @@ -113,7 +113,7 @@ protected function registerWhoopsHandler() { if ($this->shouldReturnJson()) { - $this->app['whoops.handler'] = $this->app->share(function() + $this->app->singleton('whoops.handler', function() { return new JsonResponseHandler; }); @@ -151,7 +151,7 @@ protected function requestWantsJson() */ protected function registerPrettyWhoopsHandler() { - $this->app['whoops.handler'] = $this->app->share(function() + $this->app->singleton('whoops.handler', function() { with($handler = new PrettyPageHandler)->setEditor('sublime'); diff --git a/src/Illuminate/Mail/MailServiceProvider.php b/src/Illuminate/Mail/MailServiceProvider.php index 4728a1d0..396fab30 100755 --- a/src/Illuminate/Mail/MailServiceProvider.php +++ b/src/Illuminate/Mail/MailServiceProvider.php @@ -120,7 +120,7 @@ public function registerSymfonyMailer(): void */ protected function registerSmtpTransport(array $config): void { - $this->app['symfony.transport'] = $this->app->share(function($app) use ($config) + $this->app->singleton('symfony.transport', function($app) use ($config) { $factory = new EsmtpTransportFactory(); @@ -166,7 +166,7 @@ protected function registerSmtpTransport(array $config): void */ protected function registerSendmailTransport(array $config): void { - $this->app['symfony.transport'] = $this->app->share(fn($app) => new SendmailTransport( + $this->app->singleton('symfony.transport', fn($app) => new SendmailTransport( $config['path'] ?? $app['config']->get('mail.sendmail') )); } @@ -179,7 +179,7 @@ protected function registerSendmailTransport(array $config): void */ protected function registerMailTransport(array $config): void { - $this->app['symfony.transport'] = $this->app->share(fn() => new SendmailTransport()); + $this->app->singleton('symfony.transport', fn() => new SendmailTransport()); } /** diff --git a/src/Illuminate/Routing/RoutingServiceProvider.php b/src/Illuminate/Routing/RoutingServiceProvider.php index 9d1d078f..9ef8da53 100755 --- a/src/Illuminate/Routing/RoutingServiceProvider.php +++ b/src/Illuminate/Routing/RoutingServiceProvider.php @@ -27,7 +27,7 @@ public function register() */ protected function registerRouter() { - $this->app['router'] = $this->app->share(function($app) + $this->app->singleton('router', function($app) { $router = new Router($app['events'], $app); @@ -50,7 +50,7 @@ protected function registerRouter() */ protected function registerUrlGenerator() { - $this->app['url'] = $this->app->share(function($app) + $this->app->singleton('url', function($app) { // The URL generator needs the route collection that exists on the router. // Keep in mind this is an object, so we're passing by references here @@ -71,7 +71,7 @@ protected function registerUrlGenerator() */ protected function registerRedirector() { - $this->app['redirect'] = $this->app->share(function($app) + $this->app->singleton('redirect', function($app) { $redirector = new Redirector($app['url']); @@ -94,7 +94,7 @@ protected function registerRedirector() */ protected function registerResponseFactory() { - $this->app[\Illuminate\Contracts\Routing\ResponseFactory::class] = $this->app->share(function($app) + $this->app->singleton(\Illuminate\Contracts\Routing\ResponseFactory::class, function($app) { return new ResponseFactory($app); }); From f63ca1206db851eb6e654b8582acda44239b381f Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 16:24:39 +0700 Subject: [PATCH 07/35] feat(container): swap Illuminate\Container for illuminate/container:^13 (task 4.1, SCC-1 window) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First SCC-1 core member cut over to a real v13 package. illuminate/container:^13 depends only on contracts (already ^13 from the seed phase), so it flips independently ahead of the support-gated remainder of the window. - composer.json: illuminate/container ^13 -> require, removed from replace. - Deleted fork src/Illuminate/Container/ tree (v13 package shadows it via the longer Illuminate\Container\ PSR-4 prefix; concrete classes now vendored). - Foundation\Application: BindingResolutionException import -> Contracts (v13 moved it there; docblock-only @throws, no runtime path). - tests/Container/ deleted (157 tests): these unit-test the container itself, now a vendored package tested upstream — the fork must not re-test a dependency. - tests/{Foundation,Mail,Exception}: bindShared()/share()/exception refs adapted. Application extends v13 Container with no signature break (make/bound/extend overrides compatible; verified via isolated class_exists probes). Fork suite green (1489, 0 fail; -157 from the removed container tests). composer.lock is gitignored. Prep landed earlier this branch: bindShared() sweep (69421972), share() sweep (f1fff797). Co-Authored-By: Claude Opus 4.8 (1M context) --- composer.json | 2 +- .../Container/BindingResolutionException.php | 3 - src/Illuminate/Container/BoundMethod.php | 247 --- .../Container/CircularDependencyException.php | 10 - src/Illuminate/Container/Container.php | 1556 ----------------- .../Container/ContainerExtendTest.php | 188 -- .../Container/ContextualBindingBuilder.php | 92 - .../Container/EntryNotFoundException.php | 8 - .../Container/RewindableGenerator.php | 61 - src/Illuminate/Container/composer.json | 25 - src/Illuminate/Foundation/Application.php | 2 +- tests/Container/ContainerCallTest.php | 295 ---- .../ContainerContextualBindingTest.php | 635 ------- tests/Container/ContainerExtendTest.php | 200 --- tests/Container/ContainerL4Test.php | 366 ---- tests/Container/ContainerNewTest.php | 690 -------- .../ContainerResolveNonInstantiableTest.php | 75 - tests/Container/ContainerTaggingTest.php | 105 -- tests/Container/ResolvingCallbackTest.php | 498 ------ tests/Container/RewindableGeneratorTest.php | 41 - tests/Exception/HandlerTest.php | 2 +- .../Foundation/FoundationApplicationTest.php | 8 +- tests/Mail/MailMailerTest.php | 2 +- 23 files changed, 8 insertions(+), 5103 deletions(-) delete mode 100644 src/Illuminate/Container/BindingResolutionException.php delete mode 100644 src/Illuminate/Container/BoundMethod.php delete mode 100644 src/Illuminate/Container/CircularDependencyException.php delete mode 100755 src/Illuminate/Container/Container.php delete mode 100644 src/Illuminate/Container/ContainerExtendTest.php delete mode 100644 src/Illuminate/Container/ContextualBindingBuilder.php delete mode 100644 src/Illuminate/Container/EntryNotFoundException.php delete mode 100644 src/Illuminate/Container/RewindableGenerator.php delete mode 100755 src/Illuminate/Container/composer.json delete mode 100644 tests/Container/ContainerCallTest.php delete mode 100644 tests/Container/ContainerContextualBindingTest.php delete mode 100644 tests/Container/ContainerExtendTest.php delete mode 100755 tests/Container/ContainerL4Test.php delete mode 100644 tests/Container/ContainerNewTest.php delete mode 100644 tests/Container/ContainerResolveNonInstantiableTest.php delete mode 100644 tests/Container/ContainerTaggingTest.php delete mode 100644 tests/Container/ResolvingCallbackTest.php delete mode 100644 tests/Container/RewindableGeneratorTest.php diff --git a/composer.json b/composer.json index 84459593..1b439753 100755 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "filp/whoops": "~2.11", "illuminate/collections": "^13", "illuminate/conditionable": "^13", + "illuminate/container": "^13", "illuminate/contracts": "^13", "illuminate/macroable": "^13", "illuminate/reflection": "^13", @@ -46,7 +47,6 @@ "illuminate/cache": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", - "illuminate/container": "self.version", "illuminate/cookie": "self.version", "illuminate/database": "self.version", "illuminate/encryption": "self.version", diff --git a/src/Illuminate/Container/BindingResolutionException.php b/src/Illuminate/Container/BindingResolutionException.php deleted file mode 100644 index 36975df2..00000000 --- a/src/Illuminate/Container/BindingResolutionException.php +++ /dev/null @@ -1,3 +0,0 @@ -make($segments[0]), $method], $parameters - ); - } - - /** - * Call a method that has been bound to the container. - * - * @param Container $container - * @param callable|string $callback - * @param mixed $default - * - * @return mixed - */ - protected static function callBoundMethod(Container $container, callable|string $callback, $default) - { - if (! is_array($callback)) { - return Util::unwrapIfClosure($default); - } - - // Here we need to turn the array callable into a Class@method string we can use to - // examine the container and see if there are any method bindings for this given - // method. If there are, we can call this method binding callback immediately. - $method = static::normalizeMethod($callback); - - if ($container->hasMethodBinding($method)) { - return $container->callMethodBinding($method, $callback[0]); - } - - // Diqo: - // This is where the array callback is executed. Note that the - // value that is used for execution is only $default, which is - // the closure that should haved "closure" in the callback and the - // required parameters. - // - // To further understand the mechanisme, see `call` on how it contrstruct - // the closure before calling this `callBoundMethod`. - // - // So, unwrapIfClosure just executed the $default closure. - return Util::unwrapIfClosure($default); - } - - /** - * Normalize the given callback into a Class@method string. - * - * @param callable $callback - * @return string - */ - protected static function normalizeMethod(callable $callback): string - { - $class = is_string($callback[0]) ? $callback[0] : get_class($callback[0]); - - return "{$class}@{$callback[1]}"; - } - - /** - * Get all dependencies for a given method. - * - * @param Container $container - * @param callable|string $callback - * @param array $parameters - * - * @return array - * - * @throws BindingResolutionException - * @throws ReflectionException - */ - protected static function getMethodDependencies( - Container $container, - callable|string $callback, - array $parameters = [] - ): array { - $dependencies = []; - - foreach (static::getCallReflector($callback)->getParameters() as $parameter) { - static::addDependencyForCallParameter($container, $parameter, $parameters, $dependencies); - } - - return array_merge($dependencies, array_values($parameters)); - } - - /** - * Get the proper reflection instance for the given callback. - * - * @param callable|string $callback - * @return \ReflectionFunctionAbstract - * - * @throws ReflectionException - */ - protected static function getCallReflector(callable|string $callback) - { - if (is_string($callback) && str_contains($callback, '::')) { - $callback = explode('::', $callback); - } elseif (is_object($callback) && ! $callback instanceof Closure) { - $callback = [$callback, '__invoke']; - } - - return is_array($callback) - ? new ReflectionMethod($callback[0], $callback[1]) - : new ReflectionFunction($callback); - } - - /** - * Get the dependency for the given call parameter. - * - * @param Container $container - * @param ReflectionParameter $parameter - * @param array $parameters - * @param array $dependencies - * - * @return void - * - * @throws BindingResolutionException - * @throws ReflectionException - */ - protected static function addDependencyForCallParameter( - Container $container, - ReflectionParameter $parameter, - array &$parameters, - array &$dependencies - ): void { - if (array_key_exists($paramName = $parameter->getName(), $parameters)) { - $dependencies[] = $parameters[$paramName]; - - unset($parameters[$paramName]); - } elseif (! is_null($className = Reflector::getParameterClassName($parameter))) { - if (array_key_exists($className, $parameters)) { - $dependencies[] = $parameters[$className]; - - unset($parameters[$className]); - } elseif ($parameter->isVariadic()) { - $variadicDependencies = $container->make($className); - - $dependencies = array_merge($dependencies, is_array($variadicDependencies) - ? $variadicDependencies - : [$variadicDependencies]); - } else { - $dependencies[] = $container->make($className); - } - } elseif ($parameter->isDefaultValueAvailable()) { - $dependencies[] = $parameter->getDefaultValue(); - } elseif (! $parameter->isOptional() && ! array_key_exists($paramName, $parameters)) { - $message = "Unable to resolve dependency [{$parameter}] in class {$parameter->getDeclaringClass()->getName()}"; - - throw new BindingResolutionException($message); - } - } - - /** - * Determine if the given string is in Class@method syntax. - * - * @param mixed $callback - * @return bool - */ - protected static function isCallableWithAtSign($callback): bool - { - return is_string($callback) && str_contains($callback, '@'); - } -} diff --git a/src/Illuminate/Container/CircularDependencyException.php b/src/Illuminate/Container/CircularDependencyException.php deleted file mode 100644 index 9b00b330..00000000 --- a/src/Illuminate/Container/CircularDependencyException.php +++ /dev/null @@ -1,10 +0,0 @@ -getAlias($c); - } - - return new ContextualBindingBuilder($this, $aliases); - } - - /** - * Add a contextual binding to the container. - * - * @param string $concrete - * @param string $abstract - * @param \Closure|string $implementation - * - * @return void - */ - public function addContextualBinding($concrete, $abstract, $implementation): void - { - $this->contextual[$concrete][$this->getAlias($abstract)] = $implementation; - } - - /** - * Determine if the given abstract type has been bound. - * - * @param string $abstract - * @return bool - */ - public function bound($abstract): bool - { - return isset($this->bindings[$abstract]) || - isset($this->instances[$abstract]) || - $this->isAlias($abstract); - } - - /** - * Determine if the container has a method binding. - * - * @param string $method - * @return bool - */ - public function hasMethodBinding($method): bool - { - return isset($this->methodBindings[$method]); - } - - /** - * Bind a callback to resolve with Container::call. - * - * @param array|string $method - * @param \Closure $callback - * @return void - */ - public function bindMethod($method, $callback): void - { - $this->methodBindings[$this->parseBindMethod($method)] = $callback; - } - - /** - * Get the method to be bound in class@method format. - * - * @param array|string $method - * @return string - */ - protected function parseBindMethod($method): string - { - if (is_array($method)) { - return $method[0].'@'.$method[1]; - } - - return $method; - } - - /** - * Get the method binding for the given method. - * - * @param string $method - * @param mixed $instance - * @return mixed - */ - public function callMethodBinding($method, $instance) - { - return call_user_func($this->methodBindings[$method], $instance, $this); - } - - /** - * {@inheritdoc} - * - * @return bool - */ - public function has(string $id): bool - { - return $this->bound($id); - } - - /** - * Determine if the given abstract type has been resolved. - * - * @param string $abstract - * @return bool - */ - public function resolved($abstract): bool - { - if ($this->isAlias($abstract)) { - $abstract = $this->getAlias($abstract); - } - - return isset($this->resolved[$abstract]) || isset($this->instances[$abstract]); - } - - /** - * Determine if a given string is an alias. - * - * @param string $name - * @return bool - */ - public function isAlias($name): bool - { - return isset($this->aliases[$name]); - } - - /** - * Register a binding with the container. - * - * @param string|array $abstract - * @param $concrete - * @param bool $shared - * - * @return void - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function bind($abstract, $concrete = null, $shared = false): void - { - // If the given types are actually an array, we will assume an alias is being - // defined and will grab this "real" abstract class name and register this - // alias with the container so that it can be used as a shortcut for it. - if (is_array($abstract)) { - [$abstract, $alias] = $this->extractAlias($abstract); - - $this->alias($abstract, $alias); - } - - $this->dropStaleInstances($abstract); - - // If no concrete type was given, we will simply set the concrete type to the - // abstract type. This will allow concrete type to be registered as shared - // without being forced to state their classes in both of the parameter. - if (is_null($concrete)) { - $concrete = $abstract; - } - - // If the factory is not a Closure, it means it is just a class name which is - // is bound into this container to the abstract type and we will just wrap - // it up inside a Closure to make things more convenient when extending. - if ( ! $concrete instanceof Closure) { - if (! is_string($concrete)) { - throw new \TypeError(self::class.'::bind(): Argument #2 ($concrete) must be of type Closure|string|null'); - } - - $concrete = $this->getClosure($abstract, $concrete); - } - - $this->bindings[$abstract] = compact('concrete', 'shared'); - - // If the abstract type was already resolved in this container we'll fire the - // rebound listener so that any objects which have already gotten resolved - // can have their copy of the object updated via the listener callbacks. - if ($this->resolved($abstract)) - { - $this->rebound($abstract); - } - } - - /** - * Get the Closure to be used when building a type. - * - * @param string $abstract - * @param string $concrete - * - * @return \Closure - */ - protected function getClosure(string $abstract, string $concrete): Closure - { - return function($c, $parameters = array()) use ($abstract, $concrete) { - $method = ($abstract === $concrete) ? 'build' : 'make'; - - return $c->$method($concrete, $parameters, false); - }; - } - - /** - * Register a binding if it hasn't already been registered. - * - * @param string $abstract - * @param \Closure|string|null $concrete - * @param bool $shared - * @return void - */ - public function bindIf($abstract, $concrete = null, $shared = false): void - { - if ( ! $this->bound($abstract)) { - $this->bind($abstract, $concrete, $shared); - } - } - - /** - * Register a shared binding in the container. - * - * @param string $abstract - * @param null $concrete - * - * @return void - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function singleton($abstract, $concrete = null): void - { - $this->bind($abstract, $concrete, true); - } - - /** - * Register a shared binding if it hasn't already been registered. - * - * @param string $abstract - * @param \Closure|string|null $concrete - * @return void - */ - public function singletonIf($abstract, $concrete = null): void - { - if (! $this->bound($abstract)) { - $this->singleton($abstract, $concrete); - } - } - - /** - * Register a scoped binding in the container. - * - * @param \Closure|string $abstract - * @param \Closure|string|null $concrete - * @return void - */ - public function scoped($abstract, $concrete = null): void - { - if (! in_array($abstract, $this->scopedInstances, true)) { - $this->scopedInstances[] = $abstract; - } - - $this->singleton($abstract, $concrete); - } - - /** - * Register a scoped binding if it hasn't already been registered. - * - * @param \Closure|string $abstract - * @param \Closure|string|null $concrete - * @return void - */ - public function scopedIf($abstract, $concrete = null): void - { - if (! $this->bound($abstract)) { - $this->scoped($abstract, $concrete); - } - } - - /** - * Wrap a Closure such that it is shared. - * - * @param \Closure $closure - * @return \Closure - */ - public function share(Closure $closure): Closure - { - return function($container) use ($closure) - { - // We'll simply declare a static variable within the Closures and if it has - // not been set we will execute the given Closures to resolve this value - // and return it back to these consumers of the method as an instance. - static $object; - - if (is_null($object)) - { - $object = $closure($container); - } - - return $object; - }; - } - - /** - * Bind a shared Closure into the container. - * - * @param string $abstract - * @param \Closure $closure - * @return void - */ - public function bindShared($abstract, Closure $closure): void - { - $this->bind($abstract, $this->share($closure), true); - } - - /** - * "Extend" an abstract type in the container. - * - * @param string $abstract - * @param \Closure $closure - * - * @return void - * - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function extend($abstract, Closure $closure): void - { - $abstract = $this->getAlias($abstract); - - if (isset($this->instances[$abstract])) { - $this->instances[$abstract] = $closure($this->instances[$abstract], $this); - - $this->rebound($abstract); - } else { - $this->extenders[$abstract][] = $closure; - - if ($this->resolved($abstract)) { - $this->rebound($abstract); - } - } - } - - /** - * Get an extender Closure for resolving a type. - * - * @deprecated - * @param string $abstract - * @param \Closure $closure - * @return \Closure - */ - protected function getExtender($abstract, Closure $closure): Closure - { - // To "extend" a binding, we will grab the old "resolver" Closure and pass it - // into a new one. The old resolver will be called first and the result is - // handed off to the "new" resolver, along with this container instance. - $resolver = $this->bindings[$abstract]['concrete']; - - return function($container) use ($resolver, $closure) - { - return $closure($resolver($container), $container); - }; - } - - /** - * Register an existing instance as shared in the container. - * - * @param string|array $abstract - * @param mixed $instance - * - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function instance($abstract, $instance) - { - // First, we will extract the alias from the abstract if it is an array so we - // are using the correct name when binding the type. If we get an alias it - // will be registered with the container so we can resolve it out later. - if (is_array($abstract)) - { - [$abstract, $alias] = $this->extractAlias($abstract); - - $this->alias($abstract, $alias); - } - - $this->removeAbstractAlias($abstract); - - unset($this->aliases[$abstract]); - - // We'll check to determine if this type has been bound before, and if it has - // we will fire the rebound callbacks registered with the container and it - // can be updated with consuming classes that have gotten resolved here. - $isBound = $this->bound($abstract); - - $this->instances[$abstract] = $instance; - - if ($isBound) { - $this->rebound($abstract); - } - - return $instance; - } - - /** - * Remove an alias from the contextual binding alias cache. - * - * @param string $searched - * @return void - */ - protected function removeAbstractAlias($searched): void - { - if (! isset($this->aliases[$searched])) { - return; - } - - foreach ($this->abstractAliases as $abstract => $aliases) { - foreach ($aliases as $index => $alias) { - if ($alias == $searched) { - unset($this->abstractAliases[$abstract][$index]); - } - } - } - } - - /** - * Assign a set of tags to a given binding. - * - * @param array|string $abstracts - * @param array|mixed ...$tags - * @return void - */ - public function tag($abstracts, $tags): void - { - $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1); - - foreach ($tags as $tag) { - if (! isset($this->tags[$tag])) { - $this->tags[$tag] = []; - } - - foreach ((array) $abstracts as $abstract) { - $this->tags[$tag][] = $abstract; - } - } - } - - /** - * Resolve all of the bindings for a given tag. - * - * @param string $tag - */ - public function tagged($tag) - { - if (! isset($this->tags[$tag])) { - return []; - } - - return new RewindableGenerator(function () use ($tag) { - foreach ($this->tags[$tag] as $abstract) { - yield $this->make($abstract); - } - }, count($this->tags[$tag])); - } - - /** - * Alias a type to a shorter name. - * - * @param string $abstract - * @param string $alias - * @return void - */ - public function alias($abstract, $alias): void - { - if ($alias === $abstract) { - throw new LogicException("[{$abstract}] is aliased to itself."); - } - - $this->aliases[$alias] = $abstract; - - $this->abstractAliases[$abstract][] = $alias; - } - - /** - * Extract the type and alias from a given definition. - * - * @param array $definition - * @return array - */ - protected function extractAlias(array $definition): array - { - return array(key($definition), current($definition)); - } - - /** - * Bind a new callback to an abstract's rebind event. - * - * @param string $abstract - * @param \Closure $callback - * - * @return mixed - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function rebinding($abstract, Closure $callback) - { - $this->reboundCallbacks[$abstract][] = $callback; - - if ($this->bound($abstract)) { - return $this->make($abstract); - } - } - - /** - * Refresh an instance on the given target and method. - * - * @param string $abstract - * @param mixed $target - * @param string $method - * - * @return mixed - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function refresh($abstract, $target, $method) - { - return $this->rebinding($abstract, function($app, $instance) use ($target, $method) - { - $target->{$method}($instance); - }); - } - - /** - * Fire the "rebound" callbacks for the given abstract type. - * - * @param string $abstract - * - * @return void - * @throws BindingResolutionException - * @throws ReflectionException - */ - protected function rebound($abstract): void - { - $instance = $this->make($abstract); - - foreach ($this->getReboundCallbacks($abstract) as $callback) { - $callback($this, $instance); - } - } - - /** - * Get the rebound callbacks for a given type. - * - * @param string $abstract - * @return array - */ - protected function getReboundCallbacks($abstract): array - { - if (isset($this->reboundCallbacks[$abstract])) - { - return $this->reboundCallbacks[$abstract]; - } - - return array(); - } - - /** - * Wrap the given closure such that its dependencies will be injected when executed. - * - * @param \Closure $callback - * @param array $parameters - * @return \Closure - */ - public function wrap(Closure $callback, array $parameters = []): Closure - { - return function () use ($callback, $parameters) { - return $this->call($callback, $parameters); - }; - } - - /** - * Call the given Closure / class@method and inject its dependencies. - * - * @param callable|string $callback - * @param array $parameters - * @param null $defaultMethod - * - * @return mixed - * - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function call($callback, array $parameters = [], $defaultMethod = null) - { - return BoundMethod::call($this, $callback, $parameters, $defaultMethod); - } - - /** - * Get a closure to resolve the given type from the container. - * - * @param string $abstract - * - * @return \Closure - */ - public function factory($abstract): Closure - { - return function () use ($abstract) { - return $this->make($abstract); - }; - } - - /** - * An alias function name for make(). - * - * @param string|callable $abstract - * @param array $parameters - * - * @return mixed - * - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function makeWith($abstract, array $parameters = []) - { - return $this->make($abstract, $parameters); - } - - /** - * {@inheritdoc} - * - * @param string $id - * - * @return mixed - * @throws CircularDependencyException - * @throws EntryNotFoundException - */ - public function get(string $id) - { - try { - return $this->make($id); - } catch (Exception $e) { - if ($this->has($id) || $e instanceof CircularDependencyException) { - throw $e; - } - - throw new EntryNotFoundException($id, is_int($e->getCode()) ? $e->getCode() : 0, $e); - } - } - - /** - * Resolve the given type from the container. - * - * @param string|callable $abstract - * @param array $parameters - * @param bool $raiseEvents - * - * @return mixed - * @throws BindingResolutionException - * @throws \ReflectionException - */ - public function make($abstract, $parameters = array(), $raiseEvents = true) - { - $abstract = $this->getAlias($abstract); - - // First we'll fire any event handlers which handle the "before" resolving of - // specific types. This gives some hooks the chance to add various extends - // calls to change the resolution of objects that they're interested in. - if ($raiseEvents) { - $this->fireBeforeResolvingCallbacks($abstract, $parameters); - } - - $concrete = $this->getContextualConcrete($abstract); - - $needsContextualBuild = ! empty($parameters) || ! is_null($concrete); - - // If an instance of the type is currently being managed as a singleton we'll - // just return an existing instance instead of instantiating new instances - // so the developer can keep using the same objects instance every time. - if (isset($this->instances[$abstract]) && ! $needsContextualBuild) { - return $this->instances[$abstract]; - } - - if (is_null($concrete)) { - $concrete = $this->getConcrete($abstract); - } - - // We're ready to instantiate an instance of the concrete type registered for - // the binding. This will instantiate the types, as well as resolve any of - // its "nested" dependencies recursively until all have gotten resolved. - if ($this->isBuildable($concrete, $abstract)) { - $object = $this->build($concrete, $parameters); - } else { - $object = $this->make($concrete, $parameters); - } - - // If we defined any extenders for this type, we'll need to spin through them - // and apply them to the object being built. This allows for the extension - // of services, such as changing configuration or decorating the object. - foreach ($this->getExtenders($abstract) as $extender) { - $object = $extender($object, $this); - } - - // If the requested type is registered as a singleton we'll want to cache off - // the instances in "memory" so we can return it later without creating an - // entirely new instance of an object on each subsequent request for it. - if ($this->isShared($abstract) && ! $needsContextualBuild) { - $this->instances[$abstract] = $object; - } - - if ($raiseEvents) { - $this->fireResolvingCallbacks($abstract, $object); - } - - // Before returning, we will also set the resolved flag to "true". - // After that we will be ready to return back the fully constructed class instance. - $this->resolved[$abstract] = true; - - return $object; - } - - /** - * Get the contextual concrete binding for the given abstract. - * - * @param string|callable $abstract - * @return \Closure|string|array|null - */ - protected function getContextualConcrete($abstract) - { - if (! is_null($binding = $this->findInContextualBindings($abstract))) { - return $binding; - } - - // Next we need to see if a contextual binding might be bound under an alias of the - // given abstract type. So, we will need to check if any aliases exist with this - // type and then spin through them and check for contextual bindings on these. - if (empty($this->abstractAliases[$abstract])) { - return null; - } - - foreach ($this->abstractAliases[$abstract] as $alias) { - if (! is_null($binding = $this->findInContextualBindings($alias))) { - return $binding; - } - } - - return null; - } - - /** - * Find the concrete binding for the given abstract in the contextual binding array. - * - * @param string|callable $abstract - * @return \Closure|string|null - */ - protected function findInContextualBindings($abstract) - { - return $this->contextual[end($this->buildStack)][$abstract] ?? null; - } - - /** - * Get the extender callbacks for a given type. - * - * @param string $abstract - * @return array - */ - protected function getExtenders($abstract): array - { - return $this->extenders[$this->getAlias($abstract)] ?? []; - } - - /** - * Remove all of the extender callbacks for a given type. - * - * @param string $abstract - * @return void - */ - public function forgetExtenders($abstract): void - { - unset($this->extenders[$this->getAlias($abstract)]); - } - - /** - * Get the concrete type for a given abstract. - * - * @param string $abstract - * - * @return mixed $concrete - */ - protected function getConcrete(string $abstract) - { - // If we don't have a registered resolver or concrete for the type, we'll just - // assume each type is a concrete name and will attempt to resolve it as is - // since the container should be able to resolve concretes automatically. - if ( ! isset($this->bindings[$abstract])) - { - if ($this->missingLeadingSlash($abstract) && isset($this->bindings['\\'.$abstract])) - { - $abstract = '\\'.$abstract; - } - - return $abstract; - } - - return $this->bindings[$abstract]['concrete']; - } - - /** - * Determine if the given abstract has a leading slash. - * - * @param string $abstract - * - * @return bool - */ - protected function missingLeadingSlash(string $abstract): bool - { - return is_string($abstract) && strpos($abstract, '\\') !== 0; - } - - /** - * Instantiate a concrete instance of the given type. - * - * @param string|Closure $concrete - * @param array $parameters - * - * @return mixed - * - * @throws BindingResolutionException - * @throws \ReflectionException - */ - public function build($concrete, $parameters = array()) - { - // If the concrete type is actually a Closure, we will just execute it and - // hand back the results of the functions, which allows functions to be - // used as resolvers for more fine-tuned resolution of these objects. - if ($concrete instanceof Closure) { - return $concrete($this, $parameters); - } - - try { - $reflector = new ReflectionClass($concrete); - } catch (ReflectionException $e) { - throw new BindingResolutionException("Target class [$concrete] does not exist.", 0, $e); - } - - // If the type is not instantiable, the developer is attempting to resolve - // an abstract type such as an Interface of Abstract Class and there is - // no binding registered for the abstractions so we need to bail out. - if ( ! $reflector->isInstantiable()) { - $this->notInstantiable($concrete); - } - - // Diqo: - // Why do we need to hold build stack? - // This is to suport variadic binding resolution. Please check - // \Illuminate\Container\Container::resolveVariadicClass - // - // The $concrete will be popped after it has been built or the container - // faced error in building it. - $this->buildStack[] = $concrete; - - $constructor = $reflector->getConstructor(); - - // If there are no constructors, that means there are no dependencies then - // we can just resolve the instances of the objects right away, without - // resolving any other types or dependencies out of these containers. - if (is_null($constructor)) { - array_pop($this->buildStack); - - return new $concrete; - } - - try { - $dependencies = $constructor->getParameters(); - - // Once we have all the constructor's parameters we can create each of the - // dependency instances and then use the reflection instances to make a - // new instance of this class, injecting the created dependencies in. - - $parameters = $this->keyParametersByArgument( - $dependencies, $parameters - ); - - $instances = $this->getDependencies( - $dependencies, $parameters - ); - } catch (BindingResolutionException $exception) { - array_pop($this->buildStack); - - throw $exception; - } - - array_pop($this->buildStack); - - return $reflector->newInstanceArgs($instances); - } - - /** - * Throw an exception that the concrete is not instantiable. - * - * @param string $concrete - * @return void - * - * @throws BindingResolutionException - */ - protected function notInstantiable($concrete): void - { - if (! empty($this->buildStack)) { - $previous = implode(', ', $this->buildStack); - - $message = "Target [$concrete] is not instantiable while building [$previous]."; - } else { - $message = "Target [$concrete] is not instantiable."; - } - - throw new BindingResolutionException($message); - } - - /** - * Resolve all of the dependencies from the ReflectionParameters. - * - * @param array $parameters - * @param array $primitives - * - * @return array - * @throws BindingResolutionException - * @throws ReflectionException - */ - protected function getDependencies(array $parameters, array $primitives = array()): array - { - $dependencies = []; - - foreach ($parameters as $parameter) - { - $dependency = Reflector::getParameterClassName($parameter); - - if (array_key_exists($parameter->name, $primitives)) { - // If the dependency has an override for this particular build we will use - // that instead as the value. Otherwise, we will continue with this run - // of resolutions and let reflection attempt to determine the result. - $dependencies[] = $primitives[$parameter->name]; - continue; - } - - if (is_null($dependency)) { - // If the class is null, it means the dependency is a string or some other - // primitive type which we can not resolve since it is not a class and - // we will just bomb out with an error since we have no-where to go. - $result = $this->resolveNonClass($parameter); - } else { - $result = $this->resolveClass($parameter); - } - - if ($parameter->isVariadic()) { - $dependencies = array_merge($dependencies, $result); - } else { - $dependencies[] = $result; - } - } - - return $dependencies; - } - - /** - * Resolve a non-class hinted dependency. - * - * @param \ReflectionParameter $parameter - * @return mixed - * - * @throws BindingResolutionException - */ - protected function resolveNonClass(ReflectionParameter $parameter) - { - if (!is_null($concrete = $this->getContextualConcrete('$' . $parameter->getName()))) { - return Util::unwrapIfClosure($concrete, $this); - } - - if ($parameter->isDefaultValueAvailable()) { - return $parameter->getDefaultValue(); - } - - $message = "Unresolvable dependency resolving [$parameter] in class {$parameter->getDeclaringClass()->getName()}"; - - throw new BindingResolutionException($message); - } - - /** - * Resolve a class based dependency from the container. - * - * @param \ReflectionParameter $parameter - * - * @return mixed - * - * @throws BindingResolutionException - * @throws ReflectionException - */ - protected function resolveClass(ReflectionParameter $parameter) - { - try { - return $parameter->isVariadic() - ? $this->resolveVariadicClass($parameter) - : $this->make(Reflector::getParameterClassName($parameter)); - } - - // If we can not resolve the class instance, we will check to see if the value - // is optional, and if it is we will return the optional parameter value as - // the value of the dependency, similarly to how we do this with scalars. - catch (BindingResolutionException $e) - { - if ($parameter->isDefaultValueAvailable()) - { - return $parameter->getDefaultValue(); - } - - if ($parameter->isVariadic()) { - return []; - } - - throw $e; - } - } - - /** - * Resolve a class based variadic dependency from the container. - * - * @param \ReflectionParameter $parameter - * - * @return mixed - * @throws BindingResolutionException - * @throws ReflectionException - */ - protected function resolveVariadicClass(ReflectionParameter $parameter) - { - $className = Reflector::getParameterClassName($parameter); - - $abstract = $this->getAlias($className); - - if (!is_array($concrete = $this->getContextualConcrete($abstract))) { - return $this->make($className); - } - - return array_map(function ($abstract) { - return $this->make($abstract); - }, $concrete); - } - - /** - * If extra parameters are passed by numeric ID, rekey them by argument name. - * - * @param array $dependencies - * @param array $parameters - * @return array - */ - protected function keyParametersByArgument(array $dependencies, array $parameters): array - { - foreach ($parameters as $key => $value) - { - if (is_numeric($key)) - { - unset($parameters[$key]); - - $parameters[$dependencies[$key]->name] = $value; - } - } - - return $parameters; - } - - /** - * Register a new before resolving callback for all types. - * - * @param \Closure|string $abstract - * @param \Closure|null $callback - * @return void - */ - public function beforeResolving($abstract, ?Closure $callback = null) - { - if (is_string($abstract)) { - $abstract = $this->getAlias($abstract); - } - - if ($abstract instanceof Closure && is_null($callback)) { - $this->globalBeforeResolvingCallbacks[] = $abstract; - } else { - $this->beforeResolvingCallbacks[$abstract][] = $callback; - } - } - - /** - * Register a new resolving callback. - * - * @param string|callable $abstract - * @param \Closure $callback - * @return void - */ - public function resolving($abstract, ?Closure $callback = null): void - { - if (is_string($abstract)) { - $abstract = $this->getAlias($abstract); - } - - if (is_null($callback) && $abstract instanceof Closure) { - // This is global callback, where it will be called - // when the container resolves any type - $this->globalResolvingCallbacks[] = $abstract; - } else { - // Call the callback when the container resolves $abstract - $this->resolvingCallbacks[$abstract][] = $callback; - } - } - - /** - * Register a new after resolving callback for all types. - * - * @param \Closure|string $abstract - * @param \Closure|null $callback - * @return void - */ - public function afterResolving($abstract, ?Closure $callback = null) - { - if (is_string($abstract)) { - $abstract = $this->getAlias($abstract); - } - - if ($abstract instanceof Closure && is_null($callback)) { - $this->globalAfterResolvingCallbacks[] = $abstract; - } else { - $this->afterResolvingCallbacks[$abstract][] = $callback; - } - } - - /** - * Register a new resolving callback for all types. - * - * @param \Closure $callback - * @return void - */ - public function resolvingAny(Closure $callback): void - { - $this->globalResolvingCallbacks[] = $callback; - } - - /** - * Fire all of the before resolving callbacks. - * - * @param string $abstract - * @param array $parameters - * @return void - */ - protected function fireBeforeResolvingCallbacks($abstract, $parameters = []): void - { - $this->fireBeforeCallbackArray($abstract, $parameters, $this->globalBeforeResolvingCallbacks); - - foreach ($this->beforeResolvingCallbacks as $type => $callbacks) { - if ($type === $abstract || is_subclass_of($abstract, $type)) { - $this->fireBeforeCallbackArray($abstract, $parameters, $callbacks); - } - } - } - - /** - * Fire an array of callbacks with an object. - * - * @param string $abstract - * @param array $parameters - * @param array $callbacks - * @return void - */ - protected function fireBeforeCallbackArray($abstract, $parameters, array $callbacks): void - { - foreach ($callbacks as $callback) { - $callback($abstract, $parameters, $this); - } - } - - /** - * Fire all of the resolving callbacks. - * - * @param string $abstract - * @param mixed $object - * @return void - */ - protected function fireResolvingCallbacks($abstract, $object): void - { - $this->fireCallbackArray($object, $this->globalResolvingCallbacks); - - $this->fireCallbackArray( - $object, $this->getCallbacksForType($abstract, $object, $this->resolvingCallbacks) - ); - - $this->fireAfterResolvingCallbacks($abstract, $object); - } - - /** - * Fire all of the after resolving callbacks. - * - * @param string $abstract - * @param mixed $object - * @return void - */ - protected function fireAfterResolvingCallbacks($abstract, $object): void - { - $this->fireCallbackArray($object, $this->globalAfterResolvingCallbacks); - - $this->fireCallbackArray( - $object, $this->getCallbacksForType($abstract, $object, $this->afterResolvingCallbacks) - ); - } - - /** - * Get all callbacks for a given type. - * - * @param string $abstract - * @param object $object - * @param array $callbacksPerType - * @return array - */ - protected function getCallbacksForType($abstract, $object, array $callbacksPerType): array - { - $results = []; - - foreach ($callbacksPerType as $type => $callbacks) { - if ($type === $abstract || $object instanceof $type) { - $results = array_merge($results, $callbacks); - } - } - - return $results; - } - - /** - * Fire an array of callbacks with an object. - * - * @param mixed $object - * @param array $callbacks - */ - protected function fireCallbackArray($object, array $callbacks): void - { - foreach ($callbacks as $callback) { - $callback($object, $this); - } - } - - /** - * Determine if a given type is shared. - * - * @param string $abstract - * - * @return bool - */ - public function isShared(string $abstract): bool - { - return isset($this->instances[$abstract]) || - (isset($this->bindings[$abstract]['shared']) && - $this->bindings[$abstract]['shared'] === true); - } - - /** - * Determine if the given concrete is buildable. - * - * @param mixed $concrete - * @param string $abstract - * @return bool - */ - protected function isBuildable($concrete, $abstract): bool - { - return $concrete === $abstract || $concrete instanceof Closure; - } - - /** - * Get the alias for an abstract if available. - * - * @param string $abstract - * - * @return string - */ - public function getAlias(string $abstract): string - { - return isset($this->aliases[$abstract]) - ? $this->getAlias($this->aliases[$abstract]) - : $abstract; - } - - /** - * Get the container's bindings. - * - * @return array - */ - public function getBindings(): array - { - return $this->bindings; - } - - /** - * Drop all of the stale instances and aliases. - * - * @param string $abstract - * @return void - */ - protected function dropStaleInstances($abstract): void - { - unset($this->instances[$abstract], $this->aliases[$abstract]); - } - - /** - * Remove a resolved instance from the instance cache. - * - * @param string $abstract - * @return void - */ - public function forgetInstance($abstract): void - { - unset($this->instances[$abstract]); - } - - /** - * Clear all of the instances from the container. - * - * @return void - */ - public function forgetInstances(): void - { - $this->instances = array(); - } - - /** - * Flush the container of all bindings and resolved instances. - * - * @return void - */ - public function flush(): void - { - $this->aliases = []; - $this->resolved = []; - $this->bindings = []; - $this->instances = []; - $this->abstractAliases = []; - } - - /** - * Determine if a given offset exists. - * - * @param string $key - * @return bool - */ - public function offsetExists($key): bool - { - return $this->bound($key); - } - - /** - * Get the value at a given offset. - * - * @param string $key - * - * @return mixed - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function offsetGet($key): mixed - { - return $this->make($key); - } - - /** - * Set the value at a given offset. - * - * @param string $key - * @param mixed $value - * - * @return void - * @throws BindingResolutionException - * @throws ReflectionException - */ - public function offsetSet($key, $value): void - { - // If the value is not a Closure, we will make it one. This simply gives - // more "drop-in" replacement functionality for the Pimple which this - // container's simplest functions are base modeled and built after. - if ( ! $value instanceof Closure) - { - $value = function() use ($value) - { - return $value; - }; - } - - $this->bind($key, $value); - } - - /** - * Unset the value at a given offset. - * - * @param string $key - * @return void - */ - public function offsetUnset($key): void - { - unset($this->bindings[$key], $this->instances[$key], $this->resolved[$key]); - } - - /** - * Dynamically access container services. - * - * @param string $key - * @return mixed - */ - public function __get($key) - { - return $this[$key]; - } - - /** - * Dynamically set container services. - * - * @param string $key - * @param mixed $value - * @return void - */ - public function __set($key, $value) - { - $this[$key] = $value; - } - -} diff --git a/src/Illuminate/Container/ContainerExtendTest.php b/src/Illuminate/Container/ContainerExtendTest.php deleted file mode 100644 index fd8ff203..00000000 --- a/src/Illuminate/Container/ContainerExtendTest.php +++ /dev/null @@ -1,188 +0,0 @@ -extend('foo', function ($old, $container) { - return $old.'bar'; - }); - - $this->assertSame('foobar', $container->make('foo')); - - $container = new Container; - - $container->singleton('foo', function () { - return (object) ['name' => 'taylor']; - }); - $container->extend('foo', function ($old, $container) { - $old->age = 26; - - return $old; - }); - - $result = $container->make('foo'); - - $this->assertSame('taylor', $result->name); - $this->assertEquals(26, $result->age); - $this->assertSame($result, $container->make('foo')); - } - - public function testExtendInstancesArePreserved(): void - { - $container = new Container; - $container->bind('foo', function () { - $obj = new stdClass; - $obj->foo = 'bar'; - - return $obj; - }); - - $obj = new stdClass; - $obj->foo = 'foo'; - $container->instance('foo', $obj); - $container->extend('foo', function ($obj, $container) { - $obj->bar = 'baz'; - - return $obj; - }); - $container->extend('foo', function ($obj, $container) { - $obj->baz = 'foo'; - - return $obj; - }); - - $this->assertSame('foo', $container->make('foo')->foo); - $this->assertSame('baz', $container->make('foo')->bar); - $this->assertSame('foo', $container->make('foo')->baz); - } - - public function testExtendIsLazyInitialized(): void - { - ContainerLazyExtendStub::$initialized = false; - - $container = new Container; - $container->bind(ContainerLazyExtendStub::class); - $container->extend(ContainerLazyExtendStub::class, function ($obj, $container) { - $obj->init(); - - return $obj; - }); - $this->assertFalse(ContainerLazyExtendStub::$initialized); - $container->make(ContainerLazyExtendStub::class); - $this->assertTrue(ContainerLazyExtendStub::$initialized); - } - - public function testExtendInstanceRebindingCallback(): void - { - $_SERVER['_test_rebind'] = false; - - $container = new Container; - $container->rebinding('foo', function () { - $_SERVER['_test_rebind'] = true; - }); - - $obj = new stdClass; - $container->instance('foo', $obj); - - $container->extend('foo', function ($obj, $container) { - return $obj; - }); - - $this->assertTrue($_SERVER['_test_rebind']); - } - - public function testExtendBindRebindingCallback(): void - { - $_SERVER['_test_rebind'] = false; - - $container = new Container; - $container->rebinding('foo', function () { - $_SERVER['_test_rebind'] = true; - }); - $container->bind('foo', function () { - return new stdClass; - }); - - $this->assertFalse($_SERVER['_test_rebind']); - - $container->make('foo'); - - $container->extend('foo', function ($obj, $container) { - return $obj; - }); - - $this->assertTrue($_SERVER['_test_rebind']); - } - - public function testExtensionWorksOnAliasedBindings(): void - { - $container = new Container; - $container->singleton('something', function () { - return 'some value'; - }); - $container->alias('something', 'something-alias'); - $container->extend('something-alias', function ($value) { - return $value.' extended'; - }); - - $this->assertSame('some value extended', $container->make('something')); - } - - public function testMultipleExtends(): void - { - $container = new Container; - $container['foo'] = 'foo'; - $container->extend('foo', function ($old, $container) { - return $old.'bar'; - }); - $container->extend('foo', function ($old, $container) { - return $old.'baz'; - }); - - $this->assertSame('foobarbaz', $container->make('foo')); - } - - public function testUnsetExtend(): void - { - $container = new Container; - $container->bind('foo', function () { - $obj = new stdClass; - $obj->foo = 'bar'; - - return $obj; - }); - - $container->extend('foo', function ($obj, $container) { - $obj->bar = 'baz'; - - return $obj; - }); - - unset($container['foo']); - $container->forgetExtenders('foo'); - - $container->bind('foo', function () { - return 'foo'; - }); - - $this->assertSame('foo', $container->make('foo')); - } -} - -class ContainerLazyExtendStub -{ - public static $initialized = false; - - public function init(): void - { - static::$initialized = true; - } -} diff --git a/src/Illuminate/Container/ContextualBindingBuilder.php b/src/Illuminate/Container/ContextualBindingBuilder.php deleted file mode 100644 index b5995b62..00000000 --- a/src/Illuminate/Container/ContextualBindingBuilder.php +++ /dev/null @@ -1,92 +0,0 @@ -concrete = $concrete; - $this->container = $container; - } - - /** - * Define the abstract target that depends on the context. - * - * @param string $abstract - * - * @return ContextualBindingBuilder - */ - public function needs(string $abstract): ContextualBindingBuilder - { - $this->needs = $abstract; - - return $this; - } - - /** - * Define the implementation for the contextual binding. - * - * @param \Closure|string|array $implementation - * @return void - */ - public function give($implementation): void - { - foreach (Arr::wrap($this->concrete) as $concrete) { - $this->container->addContextualBinding($concrete, $this->needs, $implementation); - } - } - - /** - * Define tagged services to be used as the implementation for the contextual binding. - * - * @param string $tag - * @return void - */ - public function giveTagged($tag): void - { - $this->give(function ($container) use ($tag) { - $taggedServices = $container->tagged($tag); - - return is_array($taggedServices) ? $taggedServices : iterator_to_array($taggedServices); - }); - } - - /** - * Specify the configuration item to bind as a primitive. - * - * @param string $key - * @param mixed $default - * @return void - */ - public function giveConfig($key, $default = null): void - { - $this->give(fn ($container) => $container['config']->get($key, $default)); - } -} \ No newline at end of file diff --git a/src/Illuminate/Container/EntryNotFoundException.php b/src/Illuminate/Container/EntryNotFoundException.php deleted file mode 100644 index bb2ef2ad..00000000 --- a/src/Illuminate/Container/EntryNotFoundException.php +++ /dev/null @@ -1,8 +0,0 @@ -count = $count; - $this->generator = $generator; - } - - /** - * Get an iterator from the generator. - * - * @return \Traversable - */ - public function getIterator(): Traversable - { - return ($this->generator)(); - } - - /** - * Get the total number of tagged services. - * - * @return int - */ - public function count(): int - { - if (is_callable($count = $this->count)) { - $this->count = $count(); - } - - return $this->count; - } -} diff --git a/src/Illuminate/Container/composer.json b/src/Illuminate/Container/composer.json deleted file mode 100755 index a2445f18..00000000 --- a/src/Illuminate/Container/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "illuminate/container", - "license": "MIT", - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "require": { - "php": ">=5.4.0" - }, - "autoload": { - "psr-0": { - "Illuminate\\Container": "" - } - }, - "target-dir": "Illuminate/Container", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "minimum-stability": "dev" -} diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index e461fe62..6e7796b3 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -2,7 +2,7 @@ use Closure; use Illuminate\Support\Arr; -use Illuminate\Container\BindingResolutionException; +use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Foundation\Http\MiddlewareBuilder; use ReflectionException; use Illuminate\Http\Request; diff --git a/tests/Container/ContainerCallTest.php b/tests/Container/ContainerCallTest.php deleted file mode 100644 index 41fe88f3..00000000 --- a/tests/Container/ContainerCallTest.php +++ /dev/null @@ -1,295 +0,0 @@ -expectException(Error::class); - $this->expectExceptionMessage('Call to undefined function ContainerTestCallStub()'); - - $container = new Container; - $container->call('ContainerTestCallStub'); - } - - public function testCallWithAtSignBasedClassReferences(): void - { - $container = new Container; - $result = $container->call(ContainerTestCallStub::class.'@work', ['foo', 'bar']); - $this->assertEquals(['foo', 'bar'], $result); - - $container = new Container; - $result = $container->call(ContainerTestCallStub::class.'@inject'); - $this->assertInstanceOf(ContainerCallConcreteStub::class, $result[0]); - $this->assertSame('taylor', $result[1]); - - $container = new Container; - $result = $container->call(ContainerTestCallStub::class.'@inject', ['default' => 'foo']); - $this->assertInstanceOf(ContainerCallConcreteStub::class, $result[0]); - $this->assertSame('foo', $result[1]); - - $container = new Container; - $result = $container->call(ContainerTestCallStub::class, ['foo', 'bar'], 'work'); - $this->assertEquals(['foo', 'bar'], $result); - } - - public function testCallWithCallableArray(): void - { - $container = new Container; - $stub = new ContainerTestCallStub; - $result = $container->call([$stub, 'work'], ['foo', 'bar']); - $this->assertEquals(['foo', 'bar'], $result); - } - - public function testCallWithStaticMethodNameString(): void - { - $container = new Container; - $result = $container->call('Illuminate\Tests\Container\ContainerStaticMethodStub::inject'); - $this->assertInstanceOf(ContainerCallConcreteStub::class, $result[0]); - $this->assertSame('taylor', $result[1]); - } - - public function testCallWithGlobalMethodName(): void - { - $container = new Container; - $result = $container->call('Illuminate\Tests\Container\containerTestInject'); - $this->assertInstanceOf(ContainerCallConcreteStub::class, $result[0]); - $this->assertSame('taylor', $result[1]); - } - - public function testCallWithBoundMethod(): void - { - $container = new Container; - $container->bindMethod(ContainerTestCallStub::class.'@unresolvable', function ($stub) { - return $stub->unresolvable('foo', 'bar'); - }); - $result = $container->call(ContainerTestCallStub::class.'@unresolvable'); - $this->assertEquals(['foo', 'bar'], $result); - - $container = new Container; - $container->bindMethod(ContainerTestCallStub::class.'@unresolvable', function ($stub) { - return $stub->unresolvable('foo', 'bar'); - }); - $result = $container->call([new ContainerTestCallStub, 'unresolvable']); - $this->assertEquals(['foo', 'bar'], $result); - - $container = new Container; - $result = $container->call([new ContainerTestCallStub, 'inject'], ['_stub' => 'foo', 'default' => 'bar']); - $this->assertInstanceOf(ContainerCallConcreteStub::class, $result[0]); - $this->assertSame('bar', $result[1]); - - $container = new Container; - $result = $container->call([new ContainerTestCallStub, 'inject'], ['_stub' => 'foo']); - $this->assertInstanceOf(ContainerCallConcreteStub::class, $result[0]); - $this->assertSame('taylor', $result[1]); - } - - public function testBindMethodAcceptsAnArray(): void - { - $container = new Container; - $container->bindMethod([ContainerTestCallStub::class, 'unresolvable'], function ($stub) { - return $stub->unresolvable('foo', 'bar'); - }); - $result = $container->call(ContainerTestCallStub::class.'@unresolvable'); - $this->assertEquals(['foo', 'bar'], $result); - - $container = new Container; - $container->bindMethod([ContainerTestCallStub::class, 'unresolvable'], function ($stub) { - return $stub->unresolvable('foo', 'bar'); - }); - $result = $container->call([new ContainerTestCallStub, 'unresolvable']); - $this->assertEquals(['foo', 'bar'], $result); - } - - public function testClosureCallWithInjectedDependency(): void - { - $container = new Container; - $container->call(function (ContainerCallConcreteStub $stub) { - // - }, ['foo' => 'bar']); - - $container->call(function (ContainerCallConcreteStub $stub) { - // - }, ['foo' => 'bar', 'stub' => new ContainerCallConcreteStub]); - } - - public function testCallWithDependencies(): void - { - $container = new Container; - $result = $container->call(function (stdClass $foo, $bar = []) { - return func_get_args(); - }); - - $this->assertInstanceOf(stdClass::class, $result[0]); - $this->assertEquals([], $result[1]); - - $result = $container->call(function (stdClass $foo, $bar = []) { - return func_get_args(); - }, ['bar' => 'taylor']); - - $this->assertInstanceOf(stdClass::class, $result[0]); - $this->assertSame('taylor', $result[1]); - - $stub = new ContainerCallConcreteStub; - $result = $container->call(function (stdClass $foo, ContainerCallConcreteStub $bar) { - return func_get_args(); - }, [ContainerCallConcreteStub::class => $stub]); - - $this->assertInstanceOf(stdClass::class, $result[0]); - $this->assertSame($stub, $result[1]); - - /* - * Wrap a function... - */ - $result = $container->wrap(function (stdClass $foo, $bar = []) { - return func_get_args(); - }, ['bar' => 'taylor']); - - $this->assertInstanceOf(Closure::class, $result); - $result = $result(); - - $this->assertInstanceOf(stdClass::class, $result[0]); - $this->assertSame('taylor', $result[1]); - } - - public function testCallWithVariadicDependency(): void - { - $stub1 = new ContainerCallConcreteStub; - $stub2 = new ContainerCallConcreteStub; - - $container = new Container; - $container->bind(ContainerCallConcreteStub::class, function () use ($stub1, $stub2) { - return [ - $stub1, - $stub2, - ]; - }); - - $result = $container->call(function (stdClass $foo, ContainerCallConcreteStub ...$bar) { - return func_get_args(); - }); - - $this->assertInstanceOf(stdClass::class, $result[0]); - $this->assertInstanceOf(ContainerCallConcreteStub::class, $result[1]); - $this->assertSame($stub1, $result[1]); - $this->assertSame($stub2, $result[2]); - } - - public function testCallWithCallableObject(): void - { - $container = new Container; - $callable = new ContainerCallCallableStub; - $result = $container->call($callable); - $this->assertInstanceOf(ContainerCallConcreteStub::class, $result[0]); - $this->assertSame('jeffrey', $result[1]); - } - - public function testCallWithCallableClassString(): void - { - $container = new Container; - $result = $container->call(ContainerCallCallableClassStringStub::class); - $this->assertInstanceOf(ContainerCallConcreteStub::class, $result[0]); - $this->assertSame('jeffrey', $result[1]); - $this->assertInstanceOf(ContainerTestCallStub::class, $result[2]); - } - - public function testCallWithoutRequiredParamsThrowsException(): void - { - $this->expectException(BindingResolutionException::class); - $this->expectExceptionMessage('Unable to resolve dependency [Parameter #0 [ $foo ]] in class Illuminate\Tests\Container\ContainerTestCallStub'); - - $container = new Container; - $container->call(ContainerTestCallStub::class.'@unresolvable'); - } - - public function testCallWithUnnamedParametersThrowsException(): void - { - $this->expectException(BindingResolutionException::class); - $this->expectExceptionMessage('Unable to resolve dependency [Parameter #0 [ $foo ]] in class Illuminate\Tests\Container\ContainerTestCallStub'); - - $container = new Container; - $container->call([new ContainerTestCallStub, 'unresolvable'], ['foo', 'bar']); - } - - public function testCallWithoutRequiredParamsOnClosureThrowsException(): void - { - $this->expectException(BindingResolutionException::class); - $this->expectExceptionMessage('Unable to resolve dependency [Parameter #0 [ $foo ]] in class Illuminate\Tests\Container\ContainerCallTest'); - - $container = new Container; - $container->call(function ($foo, $bar = 'default') { - return $foo; - }); - } -} - -class ContainerTestCallStub -{ - public function work(): array - { - return func_get_args(); - } - - public function inject(ContainerCallConcreteStub $stub, $default = 'taylor'): array - { - return func_get_args(); - } - - public function unresolvable($foo, $bar): array - { - return func_get_args(); - } -} - -class ContainerCallConcreteStub -{ - // -} - -function containerTestInject(ContainerCallConcreteStub $stub, $default = 'taylor') -{ - return func_get_args(); -} - -class ContainerStaticMethodStub -{ - public static function inject(ContainerCallConcreteStub $stub, $default = 'taylor'): array - { - return func_get_args(); - } -} - -class ContainerCallCallableStub -{ - public function __invoke(ContainerCallConcreteStub $stub, $default = 'jeffrey') - { - return func_get_args(); - } -} - -class ContainerCallCallableClassStringStub -{ - public $stub; - - public $default; - - public function __construct(ContainerCallConcreteStub $stub, $default = 'jeffrey') - { - $this->stub = $stub; - $this->default = $default; - } - - public function __invoke(ContainerTestCallStub $dependency) - { - return [$this->stub, $this->default, $dependency]; - } -} \ No newline at end of file diff --git a/tests/Container/ContainerContextualBindingTest.php b/tests/Container/ContainerContextualBindingTest.php deleted file mode 100644 index bba57647..00000000 --- a/tests/Container/ContainerContextualBindingTest.php +++ /dev/null @@ -1,635 +0,0 @@ -bind(IContainerContextContractStub::class, ContainerContextImplementationStub::class); - - $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStub::class); - $container->when(ContainerTestContextInjectTwo::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class); - - $one = $container->make(ContainerTestContextInjectOne::class); - $two = $container->make(ContainerTestContextInjectTwo::class); - - $this->assertInstanceOf(ContainerContextImplementationStub::class, $one->impl); - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $two->impl); - - /* - * Test With Closures - */ - $container = new Container; - - $container->bind(IContainerContextContractStub::class, ContainerContextImplementationStub::class); - - $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStub::class); - $container->when(ContainerTestContextInjectTwo::class)->needs(IContainerContextContractStub::class)->give(function ($container) { - return $container->make(ContainerContextImplementationStubTwo::class); - }); - - $one = $container->make(ContainerTestContextInjectOne::class); - $two = $container->make(ContainerTestContextInjectTwo::class); - - $this->assertInstanceOf(ContainerContextImplementationStub::class, $one->impl); - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $two->impl); - } - - public function testContextualBindingWorksForExistingInstancedBindings(): void - { - $container = new Container; - - $container->instance(IContainerContextContractStub::class, new ContainerContextImplementationStub); - - $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class); - - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $container->make(ContainerTestContextInjectOne::class)->impl); - } - - public function testContextualBindingWorksForNewlyInstancedBindings(): void - { - $container = new Container; - - $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class); - - $container->instance(IContainerContextContractStub::class, new ContainerContextImplementationStub); - - $this->assertInstanceOf( - ContainerContextImplementationStubTwo::class, - $container->make(ContainerTestContextInjectOne::class)->impl - ); - } - - public function testContextualBindingWorksOnExistingAliasedInstances(): void - { - $container = new Container; - - $container->instance('stub', new ContainerContextImplementationStub); - $container->alias('stub', IContainerContextContractStub::class); - - $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class); - - $this->assertInstanceOf( - ContainerContextImplementationStubTwo::class, - $container->make(ContainerTestContextInjectOne::class)->impl - ); - } - - public function testContextualBindingWorksOnNewAliasedInstances(): void - { - $container = new Container; - - $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class); - - $container->instance('stub', new ContainerContextImplementationStub); - $container->alias('stub', IContainerContextContractStub::class); - - $this->assertInstanceOf( - ContainerContextImplementationStubTwo::class, - $container->make(ContainerTestContextInjectOne::class)->impl - ); - } - - public function testContextualBindingWorksOnNewAliasedBindings(): void - { - $container = new Container; - - $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class); - - $container->bind('stub', ContainerContextImplementationStub::class); - $container->alias('stub', IContainerContextContractStub::class); - - $this->assertInstanceOf( - ContainerContextImplementationStubTwo::class, - $container->make(ContainerTestContextInjectOne::class)->impl - ); - } - - public function testContextualBindingWorksForMultipleClasses(): void - { - $container = new Container; - - $container->bind(IContainerContextContractStub::class, ContainerContextImplementationStub::class); - - $container->when([ContainerTestContextInjectTwo::class, ContainerTestContextInjectThree::class])->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class); - - $this->assertInstanceOf( - ContainerContextImplementationStub::class, - $container->make(ContainerTestContextInjectOne::class)->impl - ); - - $this->assertInstanceOf( - ContainerContextImplementationStubTwo::class, - $container->make(ContainerTestContextInjectTwo::class)->impl - ); - - $this->assertInstanceOf( - ContainerContextImplementationStubTwo::class, - $container->make(ContainerTestContextInjectThree::class)->impl - ); - } - - public function testContextualBindingDoesntOverrideNonContextualResolution(): void - { - $container = new Container; - - $container->instance('stub', new ContainerContextImplementationStub); - $container->alias('stub', IContainerContextContractStub::class); - - $container->when(ContainerTestContextInjectTwo::class)->needs(IContainerContextContractStub::class)->give(ContainerContextImplementationStubTwo::class); - - $this->assertInstanceOf( - ContainerContextImplementationStubTwo::class, - $container->make(ContainerTestContextInjectTwo::class)->impl - ); - - $this->assertInstanceOf( - ContainerContextImplementationStub::class, - $container->make(ContainerTestContextInjectOne::class)->impl - ); - } - - public function testContextuallyBoundInstancesAreNotUnnecessarilyRecreated(): void - { - ContainerTestContextInjectInstantiations::$instantiations = 0; - - $container = new Container; - - $container->instance(IContainerContextContractStub::class, new ContainerContextImplementationStub); - $container->instance(ContainerTestContextInjectInstantiations::class, new ContainerTestContextInjectInstantiations); - - $this->assertEquals(1, ContainerTestContextInjectInstantiations::$instantiations); - - $container->when(ContainerTestContextInjectOne::class)->needs(IContainerContextContractStub::class)->give(ContainerTestContextInjectInstantiations::class); - - $container->make(ContainerTestContextInjectOne::class); - $container->make(ContainerTestContextInjectOne::class); - $container->make(ContainerTestContextInjectOne::class); - $container->make(ContainerTestContextInjectOne::class); - - $this->assertEquals(1, ContainerTestContextInjectInstantiations::$instantiations); - } - - public function testContainerCanInjectSimpleVariable(): void - { - $container = new Container; - $container->when(ContainerInjectVariableStub::class)->needs('$something')->give(100); - $instance = $container->make(ContainerInjectVariableStub::class); - $this->assertEquals(100, $instance->something); - - $container = new Container; - $container->when(ContainerInjectVariableStub::class)->needs('$something')->give(function ($container) { - return $container->make(ContainerContextualConcreteStub::class); - }); - $instance = $container->make(ContainerInjectVariableStub::class); - $this->assertInstanceOf(ContainerContextualConcreteStub::class, $instance->something); - } - - public function testContextualBindingWorksWithAliasedTargets(): void - { - $container = new Container; - - $container->bind(IContainerContextContractStub::class, ContainerContextImplementationStub::class); - $container->alias(IContainerContextContractStub::class, 'interface-stub'); - - $container->alias(ContainerContextImplementationStub::class, 'stub-1'); - - $container->when(ContainerTestContextInjectOne::class)->needs('interface-stub')->give('stub-1'); - $container->when(ContainerTestContextInjectTwo::class)->needs('interface-stub')->give(ContainerContextImplementationStubTwo::class); - - $one = $container->make(ContainerTestContextInjectOne::class); - $two = $container->make(ContainerTestContextInjectTwo::class); - - $this->assertInstanceOf(ContainerContextImplementationStub::class, $one->impl); - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $two->impl); - } - - public function testContextualBindingWorksForNestedOptionalDependencies(): void - { - $container = new Container; - - $container->when(ContainerTestContextInjectTwoInstances::class)->needs(ContainerTestContextInjectTwo::class)->give(function () { - return new ContainerTestContextInjectTwo(new ContainerContextImplementationStubTwo); - }); - - $resolvedInstance = $container->make(ContainerTestContextInjectTwoInstances::class); - $this->assertInstanceOf( - ContainerTestContextWithOptionalInnerDependency::class, - $resolvedInstance->implOne - ); - $this->assertNull($resolvedInstance->implOne->inner); - - $this->assertInstanceOf( - ContainerTestContextInjectTwo::class, - $resolvedInstance->implTwo - ); - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->implTwo->impl); - } - - public function testContextualBindingWorksForVariadicDependencies(): void - { - $container = new Container; - - $container->when(ContainerTestContextInjectVariadic::class)->needs(IContainerContextContractStub::class)->give(function ($c) { - return [ - $c->make(ContainerContextImplementationStub::class), - $c->make(ContainerContextImplementationStubTwo::class), - ]; - }); - - $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class); - - $this->assertCount(2, $resolvedInstance->stubs); - $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]); - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]); - } - - public function testContextualBindingWorksForVariadicDependenciesWithNothingBound(): void - { - $container = new Container; - - $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class); - - $this->assertCount(0, $resolvedInstance->stubs); - } - - public function testContextualBindingWorksForVariadicAfterNonVariadicDependencies(): void - { - $container = new Container; - - $container->when(ContainerTestContextInjectVariadicAfterNonVariadic::class)->needs(IContainerContextContractStub::class)->give(function ($c) { - return [ - $c->make(ContainerContextImplementationStub::class), - $c->make(ContainerContextImplementationStubTwo::class), - ]; - }); - - $resolvedInstance = $container->make(ContainerTestContextInjectVariadicAfterNonVariadic::class); - - $this->assertCount(2, $resolvedInstance->stubs); - $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]); - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]); - } - - public function testContextualBindingWorksForVariadicAfterNonVariadicDependenciesWithNothingBound(): void - { - $container = new Container; - - $resolvedInstance = $container->make(ContainerTestContextInjectVariadicAfterNonVariadic::class); - - $this->assertCount(0, $resolvedInstance->stubs); - } - - public function testContextualBindingWorksForVariadicDependenciesWithoutFactory(): void - { - $container = new Container; - - $container->when(ContainerTestContextInjectVariadic::class)->needs(IContainerContextContractStub::class)->give([ - ContainerContextImplementationStub::class, - ContainerContextImplementationStubTwo::class, - ]); - - $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class); - - $this->assertCount(2, $resolvedInstance->stubs); - $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]); - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]); - } - - public function testContextualBindingGivesTagsForArrayWithNoTagsDefined(): void - { - $container = new Container; - - $container->when(ContainerTestContextInjectArray::class)->needs('$stubs')->giveTagged('stub'); - - $resolvedInstance = $container->make(ContainerTestContextInjectArray::class); - - $this->assertCount(0, $resolvedInstance->stubs); - } - - public function testContextualBindingGivesTagsForVariadicWithNoTagsDefined(): void - { - $container = new Container; - - $container->when(ContainerTestContextInjectVariadic::class)->needs(IContainerContextContractStub::class)->giveTagged('stub'); - - $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class); - - $this->assertCount(0, $resolvedInstance->stubs); - } - - public function testContextualBindingGivesTagsForArray(): void - { - $container = new Container; - - $container->tag([ - ContainerContextImplementationStub::class, - ContainerContextImplementationStubTwo::class, - ], ['stub']); - - $container->when(ContainerTestContextInjectArray::class)->needs('$stubs')->giveTagged('stub'); - - $resolvedInstance = $container->make(ContainerTestContextInjectArray::class); - - $this->assertCount(2, $resolvedInstance->stubs); - $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]); - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]); - } - - public function testContextualBindingGivesTagsForVariadic(): void - { - $container = new Container; - - $container->tag([ - ContainerContextImplementationStub::class, - ContainerContextImplementationStubTwo::class, - ], ['stub']); - - $container->when(ContainerTestContextInjectVariadic::class)->needs(IContainerContextContractStub::class)->giveTagged('stub'); - - $resolvedInstance = $container->make(ContainerTestContextInjectVariadic::class); - - $this->assertCount(2, $resolvedInstance->stubs); - $this->assertInstanceOf(ContainerContextImplementationStub::class, $resolvedInstance->stubs[0]); - $this->assertInstanceOf(ContainerContextImplementationStubTwo::class, $resolvedInstance->stubs[1]); - } - - public function testContextualBindingGivesValuesFromConfigOptionalValueNull(): void - { - $config = $this->getConfigWithData('test', [ - 'username' => 'laravel', - 'password' => 'hunter42' - ]); - - $container = new Container; - $container->singleton('config', fn() => $config); - - $container - ->when(ContainerTestContextInjectFromConfigIndividualValues::class) - ->needs('$username') - ->giveConfig('test.username'); - - $container - ->when(ContainerTestContextInjectFromConfigIndividualValues::class) - ->needs('$password') - ->giveConfig('test.password'); - - $resolvedInstance = $container->make(ContainerTestContextInjectFromConfigIndividualValues::class); - - $this->assertSame('laravel', $resolvedInstance->username); - $this->assertSame('hunter42', $resolvedInstance->password); - $this->assertNull($resolvedInstance->alias); - } - - public function testContextualBindingGivesValuesFromConfigOptionalValueSet(): void - { - $config = $this->getConfigWithData('test', [ - 'username' => 'laravel', - 'password' => 'hunter42', - 'alias' => 'lumen' - ]); - - $container = new Container; - $container->singleton('config', fn() => $config); - - $container - ->when(ContainerTestContextInjectFromConfigIndividualValues::class) - ->needs('$username') - ->giveConfig('test.username'); - - $container - ->when(ContainerTestContextInjectFromConfigIndividualValues::class) - ->needs('$password') - ->giveConfig('test.password'); - - $container - ->when(ContainerTestContextInjectFromConfigIndividualValues::class) - ->needs('$alias') - ->giveConfig('test.alias'); - - $resolvedInstance = $container->make(ContainerTestContextInjectFromConfigIndividualValues::class); - - $this->assertSame('laravel', $resolvedInstance->username); - $this->assertSame('hunter42', $resolvedInstance->password); - $this->assertSame('lumen', $resolvedInstance->alias); - } - - public function testContextualBindingGivesValuesFromConfigWithDefault(): void - { - $config = $this->getConfigWithData('test', [ - 'password' => 'hunter42' - ]); - - $container = new Container; - $container->singleton('config', fn() => $config); - - $container - ->when(ContainerTestContextInjectFromConfigIndividualValues::class) - ->needs('$username') - ->giveConfig('test.username', 'DEFAULT_USERNAME'); - - $container - ->when(ContainerTestContextInjectFromConfigIndividualValues::class) - ->needs('$password') - ->giveConfig('test.password'); - - $resolvedInstance = $container->make(ContainerTestContextInjectFromConfigIndividualValues::class); - - $this->assertSame('DEFAULT_USERNAME', $resolvedInstance->username); - $this->assertSame('hunter42', $resolvedInstance->password); - $this->assertNull($resolvedInstance->alias); - } - - public function testContextualBindingGivesValuesFromConfigArray(): void - { - $config = $this->getConfigWithData('test', [ - 'username' => 'laravel', - 'password' => 'hunter42', - 'alias' => 'lumen' - ]); - - $container = new Container; - $container->singleton('config', fn() => $config); - - $container - ->when(ContainerTestContextInjectFromConfigArray::class) - ->needs('$settings') - ->giveConfig('test'); - - $resolvedInstance = $container->make(ContainerTestContextInjectFromConfigArray::class); - - $this->assertSame('laravel', $resolvedInstance->settings['username']); - $this->assertSame('hunter42', $resolvedInstance->settings['password']); - $this->assertSame('lumen', $resolvedInstance->settings['alias']); - } - - private function getConfigWithData(string $groupName, array $data = []): Repository { - $loader = $this->prophesize(\Illuminate\Config\LoaderInterface::class); - $loader->load(\Prophecy\Argument::any(), $groupName, \Prophecy\Argument::any())->willReturn($data); - return new Repository($loader->reveal(), 'production'); - } -} - -class ContainerContextualConcreteStub {} - -interface IContainerContextualContractStub {} - -class ContainerContextualImplementationStub implements IContainerContextualContractStub {} - -class ContainerInjectVariableStub -{ - public $something; - - public function __construct(ContainerContextualConcreteStub $concrete, $something) - { - $this->something = $something; - } -} - -interface IContainerContextContractStub -{ - // -} - -class ContainerContextNonContractStub -{ - // -} - -class ContainerContextImplementationStub implements IContainerContextContractStub -{ - // -} - -class ContainerContextImplementationStubTwo implements IContainerContextContractStub -{ - // -} - -class ContainerTestContextInjectInstantiations implements IContainerContextContractStub -{ - public static $instantiations; - - public function __construct() - { - static::$instantiations++; - } -} - -class ContainerTestContextInjectOne -{ - public $impl; - - public function __construct(IContainerContextContractStub $impl) - { - $this->impl = $impl; - } -} - -class ContainerTestContextInjectTwo -{ - public $impl; - - public function __construct(IContainerContextContractStub $impl) - { - $this->impl = $impl; - } -} - -class ContainerTestContextInjectThree -{ - public $impl; - - public function __construct(IContainerContextContractStub $impl) - { - $this->impl = $impl; - } -} - -class ContainerTestContextInjectTwoInstances -{ - public $implOne; - public $implTwo; - - public function __construct(ContainerTestContextWithOptionalInnerDependency $implOne, ContainerTestContextInjectTwo $implTwo) - { - $this->implOne = $implOne; - $this->implTwo = $implTwo; - } -} - -class ContainerTestContextWithOptionalInnerDependency -{ - public $inner; - - public function __construct(?ContainerTestContextInjectOne $inner = null) - { - $this->inner = $inner; - } -} - -class ContainerTestContextInjectArray -{ - public $stubs; - - public function __construct(array $stubs) - { - $this->stubs = $stubs; - } -} - -class ContainerTestContextInjectVariadic -{ - public array $stubs; - - public function __construct(IContainerContextContractStub ...$stubs) - { - $this->stubs = $stubs; - } -} - -class ContainerTestContextInjectVariadicAfterNonVariadic -{ - public $other; - public $stubs; - - public function __construct(ContainerContextNonContractStub $other, IContainerContextContractStub ...$stubs) - { - $this->other = $other; - $this->stubs = $stubs; - } -} - -class ContainerTestContextInjectFromConfigIndividualValues -{ - public $username; - public $password; - public $alias = null; - - public function __construct($username, $password, $alias = null) - { - $this->username = $username; - $this->password = $password; - $this->alias = $alias; - } -} - -class ContainerTestContextInjectFromConfigArray -{ - public $settings; - - public function __construct($settings) - { - $this->settings = $settings; - } -} \ No newline at end of file diff --git a/tests/Container/ContainerExtendTest.php b/tests/Container/ContainerExtendTest.php deleted file mode 100644 index f1965902..00000000 --- a/tests/Container/ContainerExtendTest.php +++ /dev/null @@ -1,200 +0,0 @@ -extend('foo', function ($old, $container) { - return $old.'bar'; - }); - - $this->assertSame('foobar', $container->make('foo')); - - $container = new Container; - - $container->singleton('foo', function () { - return (object) ['name' => 'taylor']; - }); - $container->extend('foo', function ($old, $container) { - $old->age = 26; - - return $old; - }); - - $result = $container->make('foo'); - - $this->assertSame('taylor', $result->name); - $this->assertEquals(26, $result->age); - $this->assertSame($result, $container->make('foo')); - } - - public function testExtendInstancesArePreserved(): void - { - $container = new Container; - $container->bind('foo', function () { - $obj = new stdClass; - $obj->foo = 'bar'; - - return $obj; - }); - - $obj = new stdClass; - $obj->foo = 'foo'; - $container->instance('foo', $obj); - $container->extend('foo', function ($obj, $container) { - $obj->bar = 'baz'; - - return $obj; - }); - $container->extend('foo', function ($obj, $container) { - $obj->baz = 'foo'; - - return $obj; - }); - - $this->assertSame('foo', $container->make('foo')->foo); - $this->assertSame('baz', $container->make('foo')->bar); - $this->assertSame('foo', $container->make('foo')->baz); - } - - public function testExtendIsLazyInitialized(): void - { - ContainerExtLazyExtendStub::$initialized = false; - - $container = new Container; - $container->bind(ContainerExtLazyExtendStub::class); - $container->extend(ContainerExtLazyExtendStub::class, function ($obj, $container) { - $obj->init(); - - return $obj; - }); - $this->assertFalse(ContainerExtLazyExtendStub::$initialized); - $container->make(ContainerExtLazyExtendStub::class); - $this->assertTrue(ContainerExtLazyExtendStub::$initialized); - } - - public function testExtendCanBeCalledBeforeBind(): void - { - $container = new Container; - $container->extend('foo', function ($old, $container) { - return $old.'bar'; - }); - $container['foo'] = 'foo'; - - $this->assertSame('foobar', $container->make('foo')); - } - - public function testExtendInstanceRebindingCallback(): void - { - $_SERVER['_test_rebind'] = false; - - $container = new Container; - $container->rebinding('foo', function () { - $_SERVER['_test_rebind'] = true; - }); - - $obj = new stdClass; - $container->instance('foo', $obj); - - $container->extend('foo', function ($obj, $container) { - return $obj; - }); - - $this->assertTrue($_SERVER['_test_rebind']); - } - - public function testExtendBindRebindingCallback(): void - { - $_SERVER['_test_rebind'] = false; - - $container = new Container; - $container->rebinding('foo', function () { - $_SERVER['_test_rebind'] = true; - }); - $container->bind('foo', function () { - return new stdClass; - }); - - $this->assertFalse($_SERVER['_test_rebind']); - - $container->make('foo'); - - $container->extend('foo', function ($obj, $container) { - return $obj; - }); - - $this->assertTrue($_SERVER['_test_rebind']); - } - - public function testExtensionWorksOnAliasedBindings(): void - { - $container = new Container; - $container->singleton('something', function () { - return 'some value'; - }); - $container->alias('something', 'something-alias'); - $container->extend('something-alias', function ($value) { - return $value.' extended'; - }); - - $this->assertSame('some value extended', $container->make('something')); - } - - public function testMultipleExtends(): void - { - $container = new Container; - $container['foo'] = 'foo'; - $container->extend('foo', function ($old, $container) { - return $old.'bar'; - }); - $container->extend('foo', function ($old, $container) { - return $old.'baz'; - }); - - $this->assertSame('foobarbaz', $container->make('foo')); - } - - public function testUnsetExtend(): void - { - $container = new Container; - $container->bind('foo', function () { - $obj = new stdClass; - $obj->foo = 'bar'; - - return $obj; - }); - - $container->extend('foo', function ($obj, $container) { - $obj->bar = 'baz'; - - return $obj; - }); - - unset($container['foo']); - $container->forgetExtenders('foo'); - - $container->bind('foo', function () { - return 'foo'; - }); - - $this->assertSame('foo', $container->make('foo')); - } -} - -class ContainerExtLazyExtendStub -{ - public static $initialized = false; - - public function init(): void - { - static::$initialized = true; - } -} \ No newline at end of file diff --git a/tests/Container/ContainerL4Test.php b/tests/Container/ContainerL4Test.php deleted file mode 100755 index 95ce3a3d..00000000 --- a/tests/Container/ContainerL4Test.php +++ /dev/null @@ -1,366 +0,0 @@ -bind('name', function() { return 'Taylor'; }); - $this->assertEquals('Taylor', $container->make('name')); - } - - - public function testBindIfDoesntRegisterIfServiceAlreadyRegistered(): void - { - $container = new Container; - $container->bind('name', function() { return 'Taylor'; }); - $container->bindIf('name', function() { return 'Dayle'; }); - - $this->assertEquals('Taylor', $container->make('name')); - } - - - public function testSharedClosureResolution(): void - { - $container = new Container; - $class = new stdClass; - $container->singleton('class', function() use ($class) { return $class; }); - $this->assertSame($class, $container->make('class')); - } - - - public function testAutoConcreteResolution(): void - { - $container = new Container; - $this->assertInstanceOf( - ContainerConcreteStub::class, - $container->make(ContainerConcreteStub::class) - ); - } - - - public function testSlashesAreHandled(): void - { - $container = new Container; - $container->bind('\Foo', function() { return 'hello'; }); - $this->assertEquals('hello', $container->make('Foo')); - } - - - public function testParametersCanOverrideDependencies(): void - { - $container = new Container; - $stub = new ContainerDependentStub($mock = m::mock(IContainerContractStub::class)); - $resolved = $container->make(ContainerNestedDependentStub::class, [$stub]); - $this->assertInstanceOf(ContainerNestedDependentStub::class, $resolved); - $this->assertEquals($mock, $resolved->inner->impl); - } - - - public function testSharedConcreteResolution(): void - { - $container = new Container; - $container->singleton(ContainerConcreteStub::class); - $bindings = $container->getBindings(); - - $var1 = $container->make(ContainerConcreteStub::class); - $var2 = $container->make(ContainerConcreteStub::class); - $this->assertSame($var1, $var2); - } - - public function testSingletonIfDoesntRegisterIfBindingAlreadyRegistered(): void - { - $container = new Container; - $container->singleton('class', function () { - return new stdClass; - }); - $firstInstantiation = $container->make('class'); - $container->singletonIf('class', function () { - return new ContainerConcreteStub; - }); - $secondInstantiation = $container->make('class'); - $this->assertSame($firstInstantiation, $secondInstantiation); - } - - public function testSingletonIfDoesRegisterIfBindingNotRegisteredYet(): void - { - $container = new Container; - $container->singleton('class', function () { - return new stdClass; - }); - $container->singletonIf('otherClass', function () { - return new ContainerConcreteStub; - }); - $firstInstantiation = $container->make('otherClass'); - $secondInstantiation = $container->make('otherClass'); - $this->assertSame($firstInstantiation, $secondInstantiation); - } - - public function testAbstractToConcreteResolution(): void - { - $container = new Container; - $container->bind(IContainerContractStub::class, ContainerImplementationStub::class); - $class = $container->make(ContainerDependentStub::class); - $this->assertInstanceOf(ContainerImplementationStub::class, $class->impl); - } - - - public function testNestedDependencyResolution(): void - { - $container = new Container; - $container->bind(IContainerContractStub::class, ContainerImplementationStub::class); - $class = $container->make(ContainerNestedDependentStub::class); - $this->assertInstanceOf(ContainerDependentStub::class, $class->inner); - $this->assertInstanceOf(ContainerImplementationStub::class, $class->inner->impl); - } - - - public function testContainerIsPassedToResolvers(): void - { - $container = new Container; - $container->bind('something', function($c) { return $c; }); - $c = $container->make('something'); - $this->assertSame($c, $container); - } - - - public function testArrayAccess(): void - { - $container = new Container; - $container['something'] = function() { return 'foo'; }; - $this->assertTrue(isset($container['something'])); - $this->assertEquals('foo', $container['something']); - unset($container['something']); - $this->assertFalse(isset($container['something'])); - } - - - public function testAliases(): void - { - $container = new Container; - $container['foo'] = 'bar'; - $container->alias('foo', 'baz'); - $this->assertEquals('bar', $container->make('foo')); - $this->assertEquals('bar', $container->make('baz')); - $container->bind(['bam' => 'boom'], function() { return 'pow'; }); - $this->assertEquals('pow', $container->make('bam')); - $this->assertEquals('pow', $container->make('boom')); - $container->instance(['zoom' => 'zing'], 'wow'); - $this->assertEquals('wow', $container->make('zoom')); - $this->assertEquals('wow', $container->make('zing')); - } - - - public function testShareMethod(): void - { - $container = new Container; - $closure = $container->share(function() { return new stdClass; }); - $class1 = $closure($container); - $class2 = $closure($container); - $this->assertSame($class1, $class2); - } - - public function testBindingsCanBeOverridden(): void - { - $container = new Container; - $container['foo'] = 'bar'; - $foo = $container['foo']; - $container['foo'] = 'baz'; - $this->assertEquals('baz', $container['foo']); - } - - public function testParametersCanBePassedThroughToClosure(): void - { - $container = new Container; - $container->bind('foo', function($c, $parameters) - { - return $parameters; - }); - - $this->assertEquals([1, 2, 3], $container->make('foo', [1, 2, 3])); - } - - public function testResolutionOfDefaultParameters(): void - { - $container = new Container; - $instance = $container->make(ContainerDefaultValueStub::class); - $this->assertInstanceOf(ContainerConcreteStub::class, $instance->stub); - $this->assertEquals('taylor', $instance->default); - } - - - public function testResolvingCallbacksAreCalledForSpecificAbstracts(): void - { - $container = new Container; - $container->resolving('foo', function($object) { return $object->name = 'taylor'; }); - $container->bind('foo', function() { return new StdClass; }); - $instance = $container->make('foo'); - - $this->assertEquals('taylor', $instance->name); - } - - - public function testResolvingCallbacksAreCalled(): void - { - $container = new Container; - $container->resolvingAny(function($object) { return $object->name = 'taylor'; }); - $container->bind('foo', function() { return new StdClass; }); - $instance = $container->make('foo'); - - $this->assertEquals('taylor', $instance->name); - } - - - public function testUnsetRemoveBoundInstances(): void - { - $container = new Container; - $container->instance('object', new StdClass); - unset($container['object']); - - $this->assertFalse($container->bound('object')); - } - - - public function testReboundListeners(): void - { - unset($_SERVER['__test.rebind']); - - $container = new Container; - $container->bind('foo', function() {}); - $container->rebinding('foo', function() { $_SERVER['__test.rebind'] = true; }); - $container->bind('foo', function() {}); - - $this->assertTrue($_SERVER['__test.rebind']); - } - - - public function testReboundListenersOnInstances(): void - { - unset($_SERVER['__test.rebind']); - - $container = new Container; - $container->instance('foo', function() {}); - $container->rebinding('foo', function() { $_SERVER['__test.rebind'] = true; }); - $container->instance('foo', function() {}); - - $this->assertTrue($_SERVER['__test.rebind']); - } - - - public function testPassingSomePrimitiveParameters(): void - { - $container = new Container; - $value = $container->make(ContainerMixedPrimitiveStub::class, ['first' => 'taylor', 'last' => 'otwell']); - $this->assertInstanceOf(ContainerMixedPrimitiveStub::class, $value); - $this->assertEquals('taylor', $value->first); - $this->assertEquals('otwell', $value->last); - $this->assertInstanceOf(ContainerConcreteStub::class, $value->stub); - - $container = new Container; - $value = $container->make(ContainerMixedPrimitiveStub::class, [0 => 'taylor', 2 => 'otwell']); - $this->assertInstanceOf(ContainerMixedPrimitiveStub::class, $value); - $this->assertEquals('taylor', $value->first); - $this->assertEquals('otwell', $value->last); - $this->assertInstanceOf(ContainerConcreteStub::class, $value->stub); - } - - - public function testCreatingBoundConcreteClassPassesParameters(): void - { - $container = new Container; - $container->bind('TestAbstractClass', ContainerConstructorParameterLoggingStub::class); - $parameters = ['First', 'Second']; - $instance = $container->make('TestAbstractClass', $parameters); - $this->assertEquals($parameters, $instance->receivedParameters); - } - - - public function testInternalClassWithDefaultParameters(): void - { - $this->expectException(BindingResolutionException::class, 'Unresolvable dependency resolving [Parameter #0 [ $first ]] in class ContainerMixedPrimitiveStub'); - $container = new Container; - $parameters = []; - $container->make('ContainerMixedPrimitiveStub', $parameters); - } - - - public function testUnsetAffectsResolved(): void - { - $container = new Container; - $container->make(ContainerConcreteStub::class); - - unset($container[ContainerConcreteStub::class]); - $this->assertFalse($container->resolved(ContainerConcreteStub::class)); - } - -} - -class ContainerConcreteStub {} - -interface IContainerContractStub {} - -class ContainerImplementationStub implements IContainerContractStub {} - -class ContainerDependentStub { - public $impl; - public function __construct(IContainerContractStub $impl) - { - $this->impl = $impl; - } -} - -class ContainerNestedDependentStub { - public $inner; - public function __construct(ContainerDependentStub $inner) - { - $this->inner = $inner; - } -} - -class ContainerDefaultValueStub { - public $stub; public $default; - public function __construct(ContainerConcreteStub $stub, $default = 'taylor') - { - $this->stub = $stub; - $this->default = $default; - } -} - -class ContainerMixedPrimitiveStub { - public function __construct(public $first, public ContainerConcreteStub $stub, public $last) - {} -} - -class ContainerConstructorParameterLoggingStub { - public $receivedParameters; - - public function __construct($first, $second) - { - $this->receivedParameters = func_get_args(); - } -} - -class ContainerLazyExtendStub { - public static $initialized = false; - public function init(): void - { static::$initialized = true; } -} diff --git a/tests/Container/ContainerNewTest.php b/tests/Container/ContainerNewTest.php deleted file mode 100644 index d4fd2cfe..00000000 --- a/tests/Container/ContainerNewTest.php +++ /dev/null @@ -1,690 +0,0 @@ -assertSame($container, Container::getInstance()); - - Container::setInstance(null); - - $container2 = Container::getInstance(); - - $this->assertInstanceOf(Container::class, $container2); - $this->assertNotSame($container, $container2); - } - - public function testClosureResolution(): void - { - $container = new Container; - $container->bind('name', function () { - return 'Taylor'; - }); - $this->assertSame('Taylor', $container->make('name')); - } - - public function testBindIfDoesntRegisterIfServiceAlreadyRegistered(): void - { - $container = new Container; - $container->bind('name', function () { - return 'Taylor'; - }); - $container->bindIf('name', function () { - return 'Dayle'; - }); - - $this->assertSame('Taylor', $container->make('name')); - } - - public function testBindIfDoesRegisterIfServiceNotRegisteredYet(): void - { - $container = new Container; - $container->bind('surname', function () { - return 'Taylor'; - }); - $container->bindIf('name', function () { - return 'Dayle'; - }); - - $this->assertSame('Dayle', $container->make('name')); - } - - public function testSingletonIfDoesntRegisterIfBindingAlreadyRegistered(): void - { - $container = new Container; - $container->singleton('class', function () { - return new stdClass; - }); - $firstInstantiation = $container->make('class'); - $container->singletonIf('class', function () { - return new ContainerNewConcreteStub; - }); - $secondInstantiation = $container->make('class'); - $this->assertSame($firstInstantiation, $secondInstantiation); - } - - public function testSingletonIfDoesRegisterIfBindingNotRegisteredYet(): void - { - $container = new Container; - $container->singleton('class', function () { - return new stdClass; - }); - $container->singletonIf('otherClass', function () { - return new ContainerNewConcreteStub; - }); - $firstInstantiation = $container->make('otherClass'); - $secondInstantiation = $container->make('otherClass'); - $this->assertSame($firstInstantiation, $secondInstantiation); - } - - public function testSharedClosureResolution(): void - { - $container = new Container; - $container->singleton('class', function () { - return new stdClass; - }); - $firstInstantiation = $container->make('class'); - $secondInstantiation = $container->make('class'); - $this->assertSame($firstInstantiation, $secondInstantiation); - } - - public function testAutoConcreteResolution(): void - { - $container = new Container; - $this->assertInstanceOf(ContainerNewConcreteStub::class, $container->make(ContainerNewConcreteStub::class)); - } - - public function testSharedConcreteResolution(): void - { - $container = new Container; - $container->singleton(ContainerNewConcreteStub::class); - - $var1 = $container->make(ContainerNewConcreteStub::class); - $var2 = $container->make(ContainerNewConcreteStub::class); - $this->assertSame($var1, $var2); - } - - public function testBindFailsLoudlyWithInvalidArgument(): void - { - $this->expectException(TypeError::class); - $container = new Container; - - $concrete = new ContainerNewConcreteStub; - $container->bind(ContainerNewConcreteStub::class, $concrete); - } - - public function testAbstractToConcreteResolution(): void - { - $container = new Container; - $container->bind(IContainerNewContractStub::class, ContainerNewImplementationStub::class); - $class = $container->make(ContainerNewDependentStub::class); - $this->assertInstanceOf(ContainerNewImplementationStub::class, $class->impl); - } - - public function testNestedDependencyResolution(): void - { - $container = new Container; - $container->bind(IContainerNewContractStub::class, ContainerNewImplementationStub::class); - $class = $container->make(ContainerNewNestedDependentStub::class); - $this->assertInstanceOf(ContainerNewDependentStub::class, $class->inner); - $this->assertInstanceOf(ContainerNewImplementationStub::class, $class->inner->impl); - } - - public function testContainerIsPassedToResolvers(): void - { - $container = new Container; - $container->bind('something', function ($c) { - return $c; - }); - $c = $container->make('something'); - $this->assertSame($c, $container); - } - - public function testArrayAccess(): void - { - $container = new Container; - $container['something'] = function () { - return 'foo'; - }; - $this->assertTrue(isset($container['something'])); - $this->assertSame('foo', $container['something']); - unset($container['something']); - $this->assertFalse(isset($container['something'])); - } - - public function testAliases(): void - { - $container = new Container; - $container['foo'] = 'bar'; - $container->alias('foo', 'baz'); - $container->alias('baz', 'bat'); - $this->assertSame('bar', $container->make('foo')); - $this->assertSame('bar', $container->make('baz')); - $this->assertSame('bar', $container->make('bat')); - } - - public function testAliasesWithArrayOfParameters(): void - { - $container = new Container; - $container->bind('foo', function ($app, $config) { - return $config; - }); - $container->alias('foo', 'baz'); - $this->assertEquals([1, 2, 3], $container->make('baz', [1, 2, 3])); - } - - public function testBindingsCanBeOverridden(): void - { - $container = new Container; - $container['foo'] = 'bar'; - $container['foo'] = 'baz'; - $this->assertSame('baz', $container['foo']); - } - - public function testBindingAnInstanceReturnsTheInstance(): void - { - $container = new Container; - - $bound = new stdClass; - $resolved = $container->instance('foo', $bound); - - $this->assertSame($bound, $resolved); - } - - public function testBindingAnInstanceAsShared(): void - { - $container = new Container; - $bound = new stdClass; - $container->instance('foo', $bound); - $object = $container->make('foo'); - $this->assertSame($bound, $object); - } - - public function testResolutionOfDefaultParameters(): void - { - $container = new Container; - $instance = $container->make(ContainerNewDefaultValueStub::class); - $this->assertInstanceOf(ContainerNewConcreteStub::class, $instance->stub); - $this->assertSame('taylor', $instance->default); - } - - public function testUnsetRemoveBoundInstances(): void - { - $container = new Container; - $container->instance('object', new stdClass); - unset($container['object']); - - $this->assertFalse($container->bound('object')); - } - - public function testBoundInstanceAndAliasCheckViaArrayAccess(): void - { - $container = new Container; - $container->instance('object', new stdClass); - $container->alias('object', 'alias'); - - $this->assertTrue(isset($container['object'])); - $this->assertTrue(isset($container['alias'])); - } - - public function testReboundListeners(): void - { - unset($_SERVER['__test.rebind']); - - $container = new Container; - $container->bind('foo', function () { - // - }); - $container->rebinding('foo', function () { - $_SERVER['__test.rebind'] = true; - }); - $container->bind('foo', function () { - // - }); - - $this->assertTrue($_SERVER['__test.rebind']); - } - - public function testReboundListenersOnInstances(): void - { - unset($_SERVER['__test.rebind']); - - $container = new Container; - $container->instance('foo', function () { - // - }); - $container->rebinding('foo', function () { - $_SERVER['__test.rebind'] = true; - }); - $container->instance('foo', function () { - // - }); - - $this->assertTrue($_SERVER['__test.rebind']); - } - - public function testReboundListenersOnInstancesOnlyFiresIfWasAlreadyBound(): void - { - $_SERVER['__test.rebind'] = false; - - $container = new Container; - $container->rebinding('foo', function () { - $_SERVER['__test.rebind'] = true; - }); - $container->instance('foo', function () { - // - }); - - $this->assertFalse($_SERVER['__test.rebind']); - } - - public function testInternalClassWithDefaultParameters(): void - { - $this->expectException(BindingResolutionException::class); - $this->expectExceptionMessage('Unresolvable dependency resolving [Parameter #0 [ $first ]] in class Illuminate\Tests\Container\ContainerNewMixedPrimitiveStub'); - - $container = new Container; - $container->make(ContainerNewMixedPrimitiveStub::class, []); - } - - public function testBindingResolutionExceptionMessage(): void - { - $this->expectException(BindingResolutionException::class); - $this->expectExceptionMessage('Target [Illuminate\Tests\Container\IContainerNewContractStub] is not instantiable.'); - - $container = new Container; - $container->make(IContainerNewContractStub::class, []); - } - - public function testBindingResolutionExceptionMessageIncludesBuildStack(): void - { - $this->expectException(BindingResolutionException::class); - $this->expectExceptionMessage('Target [Illuminate\Tests\Container\IContainerNewContractStub] is not instantiable while building [Illuminate\Tests\Container\ContainerNewDependentStub].'); - - $container = new Container; - $container->make(ContainerNewDependentStub::class, []); - } - - public function testBindingResolutionExceptionMessageWhenClassDoesNotExist(): void - { - $this->expectException(BindingResolutionException::class); - $this->expectExceptionMessage('Target class [Foo\Bar\Baz\DummyClass] does not exist.'); - - $container = new Container; - $container->build('Foo\Bar\Baz\DummyClass'); - } - - public function testForgetInstanceForgetsInstance(): void - { - $container = new Container; - $containerConcreteStub = new ContainerNewConcreteStub; - $container->instance(ContainerNewConcreteStub::class, $containerConcreteStub); - $this->assertTrue($container->isShared(ContainerNewConcreteStub::class)); - $container->forgetInstance(ContainerNewConcreteStub::class); - $this->assertFalse($container->isShared(ContainerNewConcreteStub::class)); - } - - public function testForgetInstancesForgetsAllInstances(): void - { - $container = new Container; - $containerConcreteStub1 = new ContainerNewConcreteStub; - $containerConcreteStub2 = new ContainerNewConcreteStub; - $containerConcreteStub3 = new ContainerNewConcreteStub; - $container->instance('Instance1', $containerConcreteStub1); - $container->instance('Instance2', $containerConcreteStub2); - $container->instance('Instance3', $containerConcreteStub3); - $this->assertTrue($container->isShared('Instance1')); - $this->assertTrue($container->isShared('Instance2')); - $this->assertTrue($container->isShared('Instance3')); - $container->forgetInstances(); - $this->assertFalse($container->isShared('Instance1')); - $this->assertFalse($container->isShared('Instance2')); - $this->assertFalse($container->isShared('Instance3')); - } - - public function testContainerFlushFlushesAllBindingsAliasesAndResolvedInstances(): void - { - $container = new Container; - $container->bind('ConcreteStub', function () { - return new ContainerNewConcreteStub; - }, true); - $container->alias('ConcreteStub', 'ContainerConcreteStub'); - $container->make('ConcreteStub'); - $this->assertTrue($container->resolved('ConcreteStub')); - $this->assertTrue($container->isAlias('ContainerConcreteStub')); - $this->assertArrayHasKey('ConcreteStub', $container->getBindings()); - $this->assertTrue($container->isShared('ConcreteStub')); - $container->flush(); - $this->assertFalse($container->resolved('ConcreteStub')); - $this->assertFalse($container->isAlias('ContainerConcreteStub')); - $this->assertEmpty($container->getBindings()); - $this->assertFalse($container->isShared('ConcreteStub')); - } - - public function testResolvedResolvesAliasToBindingNameBeforeChecking(): void - { - $container = new Container; - $container->bind('ConcreteStub', function () { - return new ContainerNewConcreteStub; - }, true); - $container->alias('ConcreteStub', 'foo'); - - $this->assertFalse($container->resolved('ConcreteStub')); - $this->assertFalse($container->resolved('foo')); - - $container->make('ConcreteStub'); - - $this->assertTrue($container->resolved('ConcreteStub')); - $this->assertTrue($container->resolved('foo')); - } - - public function testGetAlias(): void - { - $container = new Container; - $container->alias('ConcreteStub', 'foo'); - $this->assertSame('ConcreteStub', $container->getAlias('foo')); - } - - public function testItThrowsExceptionWhenAbstractIsSameAsAlias(): void - { - $this->expectException('LogicException'); - $this->expectExceptionMessage('[name] is aliased to itself.'); - - $container = new Container; - $container->alias('name', 'name'); - } - - public function testContainerGetFactory(): void - { - $container = new Container; - $container->bind('name', function () { - return 'Taylor'; - }); - - $factory = $container->factory('name'); - $this->assertEquals($container->make('name'), $factory()); - } - - public function testMakeWithMethodIsAnAliasForMakeMethod(): void - { - $mock = $this->getMockBuilder(Container::class) - ->onlyMethods(['make']) - ->getMock(); - - $mock->expects($this->once()) - ->method('make') - ->with(ContainerNewDefaultValueStub::class, ['default' => 'laurence']) - ->willReturn(new stdClass); - - $result = $mock->makeWith(ContainerNewDefaultValueStub::class, ['default' => 'laurence']); - - $this->assertInstanceOf(stdClass::class, $result); - } - - public function testResolvingWithArrayOfParameters(): void - { - $container = new Container; - $instance = $container->make(ContainerNewDefaultValueStub::class, ['default' => 'adam']); - $this->assertSame('adam', $instance->default); - - $instance = $container->make(ContainerNewDefaultValueStub::class); - $this->assertSame('taylor', $instance->default); - - $container->bind('foo', function ($app, $config) { - return $config; - }); - - $this->assertEquals([1, 2, 3], $container->make('foo', [1, 2, 3])); - } - - public function testResolvingWithUsingAnInterface(): void - { - $container = new Container; - $container->bind(IContainerNewContractStub::class, ContainerNewInjectVariableStubWithInterfaceImplementation::class); - $instance = $container->make(IContainerNewContractStub::class, ['something' => 'laurence']); - $this->assertSame('laurence', $instance->something); - } - - public function testNestedParameterOverride(): void - { - $container = new Container; - $container->bind('foo', function ($app, $config) { - return $app->make('bar', ['name' => 'Taylor']); - }); - $container->bind('bar', function ($app, $config) { - return $config; - }); - - $this->assertEquals(['name' => 'Taylor'], $container->make('foo', ['something'])); - } - - public function testNestedParametersAreResetForFreshMake(): void - { - $container = new Container; - - $container->bind('foo', function ($app, $config) { - return $app->make('bar'); - }); - - $container->bind('bar', function ($app, $config) { - return $config; - }); - - $this->assertEquals([], $container->make('foo', ['something'])); - } - - public function testSingletonBindingsNotRespectedWithMakeParameters(): void - { - $container = new Container; - - $container->singleton('foo', function ($app, $config) { - return $config; - }); - - $this->assertEquals(['name' => 'taylor'], $container->make('foo', ['name' => 'taylor'])); - $this->assertEquals(['name' => 'abigail'], $container->make('foo', ['name' => 'abigail'])); - } - - public function testCanBuildWithoutParameterStackWithNoConstructors(): void - { - $container = new Container; - $this->assertInstanceOf(ContainerNewConcreteStub::class, $container->build(ContainerNewConcreteStub::class)); - } - - public function testCanBuildWithoutParameterStackWithConstructors(): void - { - $container = new Container; - $container->bind(IContainerNewContractStub::class, ContainerNewImplementationStub::class); - $this->assertInstanceOf(ContainerNewDependentStub::class, $container->build(ContainerNewDependentStub::class)); - } - - public function testContainerKnowsEntry(): void - { - $container = new Container; - $container->bind(IContainerNewContractStub::class, ContainerNewImplementationStub::class); - $this->assertTrue($container->has(IContainerNewContractStub::class)); - } - - public function testContainerCanBindAnyWord(): void - { - $container = new Container; - $container->bind('Taylor', stdClass::class); - $this->assertInstanceOf(stdClass::class, $container->get('Taylor')); - } - - public function testContainerCanDynamicallySetService(): void - { - $container = new Container; - $this->assertFalse(isset($container['name'])); - $container['name'] = 'Taylor'; - $this->assertTrue(isset($container['name'])); - $this->assertSame('Taylor', $container['name']); - } - - public function testUnknownEntryThrowsException(): void - { - $this->expectException(EntryNotFoundException::class); - - $container = new Container; - $container->get('Taylor'); - } - - public function testBoundEntriesThrowsContainerExceptionWhenNotResolvable(): void - { - $this->expectException(BindingResolutionException::class); - - $container = new Container; - $container->bind('Taylor', IContainerNewContractStub::class); - - $container->get('Taylor'); - } - - public function testContainerCanResolveClasses(): void - { - $container = new Container; - $class = $container->get(ContainerNewConcreteStub::class); - - $this->assertInstanceOf(ContainerNewConcreteStub::class, $class); - } - - // public function testContainerCanCatchCircularDependency() - // { - // $this->expectException(\Illuminate\Contracts\Container\CircularDependencyException::class); - - // $container = new Container; - // $container->get(CircularAStub::class); - // } -} - -class CircularAStub -{ - public function __construct(CircularBStub $b) - { - // - } -} - -class CircularBStub -{ - public function __construct(CircularCStub $c) - { - // - } -} - -class CircularCStub -{ - public function __construct(CircularAStub $a) - { - // - } -} - -class ContainerNewConcreteStub -{ - // -} - -interface IContainerNewContractStub -{ - // -} - -class ContainerNewImplementationStub implements IContainerNewContractStub -{ - // -} - -class ContainerNewImplementationStubTwo implements IContainerNewContractStub -{ - // -} - -class ContainerNewDependentStub -{ - public $impl; - - public function __construct(IContainerNewContractStub $impl) - { - $this->impl = $impl; - } -} - -class ContainerNewNestedDependentStub -{ - public $inner; - - public function __construct(ContainerNewDependentStub $inner) - { - $this->inner = $inner; - } -} - -class ContainerNewDefaultValueStub -{ - public $stub; - public $default; - - public function __construct(ContainerNewConcreteStub $stub, $default = 'taylor') - { - $this->stub = $stub; - $this->default = $default; - } -} - -class ContainerNewMixedPrimitiveStub -{ - public $first; - public $last; - public $stub; - - public function __construct($first, ContainerNewConcreteStub $stub, $last) - { - $this->stub = $stub; - $this->last = $last; - $this->first = $first; - } -} - -class ContainerNewInjectVariableStub -{ - public $something; - - public function __construct(ContainerNewConcreteStub $concrete, $something) - { - $this->something = $something; - } -} - -class ContainerNewInjectVariableStubWithInterfaceImplementation implements IContainerNewContractStub -{ - public $something; - - public function __construct(ContainerNewConcreteStub $concrete, $something) - { - $this->something = $something; - } -} \ No newline at end of file diff --git a/tests/Container/ContainerResolveNonInstantiableTest.php b/tests/Container/ContainerResolveNonInstantiableTest.php deleted file mode 100644 index 2dae6b20..00000000 --- a/tests/Container/ContainerResolveNonInstantiableTest.php +++ /dev/null @@ -1,75 +0,0 @@ -make(ParentClass::class, ['i' => 42]); - - $this->assertSame(42, $object->i); - } - - public function testResolvingNonInstantiableWithVariadicRemovesWiths(): void - { - $container = new Container; - $parent = $container->make(VariadicParentClass::class, ['i' => 42]); - - $this->assertCount(0, $parent->child->objects); - $this->assertSame(42, $parent->i); - } -} - -interface TestInterface -{ -} - -class ParentClass -{ - /** - * @var int - */ - public $i; - - public function __construct(?TestInterface $testObject = null, int $i = 0) - { - $this->i = $i; - } -} - -class VariadicParentClass -{ - /** - * @var \Illuminate\Tests\Container\ChildClass - */ - public $child; - - /** - * @var int - */ - public $i; - - public function __construct(ChildClass $child, int $i = 0) - { - $this->child = $child; - $this->i = $i; - } -} - -class ChildClass -{ - /** - * @var array - */ - public $objects; - - public function __construct(TestInterface ...$objects) - { - $this->objects = $objects; - } -} diff --git a/tests/Container/ContainerTaggingTest.php b/tests/Container/ContainerTaggingTest.php deleted file mode 100644 index 4b5ca848..00000000 --- a/tests/Container/ContainerTaggingTest.php +++ /dev/null @@ -1,105 +0,0 @@ -tag(ContainerImplementationTaggedStub::class, 'foo', 'bar'); - $container->tag(ContainerImplementationTaggedStubTwo::class, ['foo']); - - $this->assertCount(1, $container->tagged('bar')); - $this->assertCount(2, $container->tagged('foo')); - - $fooResults = []; - foreach ($container->tagged('foo') as $foo) { - $fooResults[] = $foo; - } - - $barResults = []; - foreach ($container->tagged('bar') as $bar) { - $barResults[] = $bar; - } - - $this->assertInstanceOf(ContainerImplementationTaggedStub::class, $fooResults[0]); - $this->assertInstanceOf(ContainerImplementationTaggedStub::class, $barResults[0]); - $this->assertInstanceOf(ContainerImplementationTaggedStubTwo::class, $fooResults[1]); - - $container = new Container; - $container->tag([ContainerImplementationTaggedStub::class, ContainerImplementationTaggedStubTwo::class], ['foo']); - $this->assertCount(2, $container->tagged('foo')); - - $fooResults = []; - foreach ($container->tagged('foo') as $foo) { - $fooResults[] = $foo; - } - - $this->assertInstanceOf(ContainerImplementationTaggedStub::class, $fooResults[0]); - $this->assertInstanceOf(ContainerImplementationTaggedStubTwo::class, $fooResults[1]); - - $this->assertCount(0, $container->tagged('this_tag_does_not_exist')); - } - - public function testTaggedServicesAreLazyLoaded(): void - { - $container = $this->createPartialMock(Container::class, ['make']); - $container->expects($this->once())->method('make')->willReturn(new ContainerImplementationTaggedStub); - - $container->tag(ContainerImplementationTaggedStub::class, ['foo']); - $container->tag(ContainerImplementationTaggedStubTwo::class, ['foo']); - - $fooResults = []; - foreach ($container->tagged('foo') as $foo) { - $fooResults[] = $foo; - break; - } - - $this->assertCount(2, $container->tagged('foo')); - $this->assertInstanceOf(ContainerImplementationTaggedStub::class, $fooResults[0]); - } - - public function testLazyLoadedTaggedServicesCanBeLoopedOverMultipleTimes(): void - { - $container = new Container; - $container->tag(ContainerImplementationTaggedStub::class, 'foo'); - $container->tag(ContainerImplementationTaggedStubTwo::class, ['foo']); - - $services = $container->tagged('foo'); - - $fooResults = []; - foreach ($services as $foo) { - $fooResults[] = $foo; - } - - $this->assertInstanceOf(ContainerImplementationTaggedStub::class, $fooResults[0]); - $this->assertInstanceOf(ContainerImplementationTaggedStubTwo::class, $fooResults[1]); - - $fooResults = []; - foreach ($services as $foo) { - $fooResults[] = $foo; - } - - $this->assertInstanceOf(ContainerImplementationTaggedStub::class, $fooResults[0]); - $this->assertInstanceOf(ContainerImplementationTaggedStubTwo::class, $fooResults[1]); - } -} - -interface IContainerTaggedContractStub -{ - // -} - -class ContainerImplementationTaggedStub implements IContainerTaggedContractStub -{ - // -} - -class ContainerImplementationTaggedStubTwo implements IContainerTaggedContractStub -{ - // -} diff --git a/tests/Container/ResolvingCallbackTest.php b/tests/Container/ResolvingCallbackTest.php deleted file mode 100644 index 58a93bf9..00000000 --- a/tests/Container/ResolvingCallbackTest.php +++ /dev/null @@ -1,498 +0,0 @@ -resolving('foo', function ($object) { - return $object->name = 'taylor'; - }); - $container->bind('foo', function () { - return new stdClass; - }); - $instance = $container->make('foo'); - - $this->assertSame('taylor', $instance->name); - } - - public function testResolvingCallbacksAreCalled(): void - { - $container = new Container; - $container->resolving(function ($object) { - return $object->name = 'taylor'; - }); - $container->bind('foo', function () { - return new stdClass; - }); - $instance = $container->make('foo'); - - $this->assertSame('taylor', $instance->name); - } - - public function testResolvingCallbacksAreCalledForType(): void - { - $container = new Container; - $container->resolving(stdClass::class, function ($object) { - return $object->name = 'taylor'; - }); - $container->bind('foo', function () { - return new stdClass; - }); - $instance = $container->make('foo'); - - $this->assertSame('taylor', $instance->name); - } - - public function testResolvingCallbacksShouldBeFiredWhenCalledWithAliases(): void - { - $container = new Container; - $container->alias(stdClass::class, 'std'); - $container->resolving('std', function ($object) { - return $object->name = 'taylor'; - }); - $container->bind('foo', function () { - return new stdClass; - }); - $instance = $container->make('foo'); - - $this->assertSame('taylor', $instance->name); - } - - public function testResolvingCallbacksAreCalledOnceForImplementation(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(2, $callCounter); - } - - public function testGlobalResolvingCallbacksAreCalledOnceForImplementation(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(2, $callCounter); - } - - public function testResolvingCallbacksAreCalledOnceForSingletonConcretes(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingContractStub::class, function ($object) use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - $container->bind(ResolvingImplementationStub::class); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(2, $callCounter); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(3, $callCounter); - } - - public function testResolvingCallbacksCanStillBeAddedAfterTheFirstResolution(): void - { - $container = new Container; - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $container->make(ResolvingImplementationStub::class); - - $callCounter = 0; - $container->resolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - } - - public function testResolvingCallbacksAreCanceledWhenInterfaceGetsBoundToSomeOtherConcrete(): void - { - $container = new Container; - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $callCounter = 0; - $container->resolving(ResolvingImplementationStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(1, $callCounter); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStubTwo::class); - $container->make(ResolvingContractStub::class); - $this->assertEquals(1, $callCounter); - } - - public function testResolvingCallbacksAreCalledOnceForStringAbstractions(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving('foo', function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind('foo', ResolvingImplementationStub::class); - - $container->make('foo'); - $this->assertEquals(1, $callCounter); - - $container->make('foo'); - $this->assertEquals(2, $callCounter); - } - - public function testResolvingCallbacksForConcretesAreCalledOnceForStringAbstractions(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingImplementationStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind('foo', ResolvingImplementationStub::class); - $container->bind('bar', ResolvingImplementationStub::class); - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - - $container->make('foo'); - $this->assertEquals(2, $callCounter); - - $container->make('bar'); - $this->assertEquals(3, $callCounter); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(4, $callCounter); - } - - public function testResolvingCallbacksAreCalledOnceForImplementation2(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, function () { - return new ResolvingImplementationStub; - }); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(1, $callCounter); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(2, $callCounter); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(3, $callCounter); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(4, $callCounter); - } - - public function testRebindingDoesNotAffectResolvingCallbacks(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - $container->bind(ResolvingContractStub::class, function () { - return new ResolvingImplementationStub; - }); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(1, $callCounter); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(2, $callCounter); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(3, $callCounter); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(4, $callCounter); - } - - public function testParametersPassedIntoResolvingCallbacks(): void - { - $container = new Container; - - $container->resolving(ResolvingContractStub::class, function ($obj, $app) use ($container) { - $this->assertInstanceOf(ResolvingContractStub::class, $obj); - $this->assertInstanceOf(ResolvingImplementationStubTwo::class, $obj); - $this->assertSame($container, $app); - }); - - $container->afterResolving(ResolvingContractStub::class, function ($obj, $app) use ($container) { - $this->assertInstanceOf(ResolvingContractStub::class, $obj); - $this->assertInstanceOf(ResolvingImplementationStubTwo::class, $obj); - $this->assertSame($container, $app); - }); - - $container->afterResolving(function ($obj, $app) use ($container) { - $this->assertInstanceOf(ResolvingContractStub::class, $obj); - $this->assertInstanceOf(ResolvingImplementationStubTwo::class, $obj); - $this->assertSame($container, $app); - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStubTwo::class); - $container->make(ResolvingContractStub::class); - } - - public function testResolvingCallbacksAreCallWhenRebindHappenForResolvedAbstract(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(1, $callCounter); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStubTwo::class); - $this->assertEquals(2, $callCounter); - - $container->make(ResolvingImplementationStubTwo::class); - $this->assertEquals(3, $callCounter); - - $container->bind(ResolvingContractStub::class, function () { - return new ResolvingImplementationStubTwo; - }); - $this->assertEquals(4, $callCounter); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(5, $callCounter); - } - - public function testRebindingDoesNotAffectMultipleResolvingCallbacks(): void - { - $container = new Container; - - $callCounter = 0; - - $container->resolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->resolving(ResolvingImplementationStubTwo::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - // it should call the callback for interface - $container->make(ResolvingContractStub::class); - $this->assertEquals(1, $callCounter); - - // it should call the callback for interface - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(2, $callCounter); - - // should call the callback for the interface it implements - // plus the callback for ResolvingImplementationStubTwo. - $container->make(ResolvingImplementationStubTwo::class); - $this->assertEquals(4, $callCounter); - } - - public function testResolvingCallbacksAreCalledForInterfaces(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $container->make(ResolvingContractStub::class); - - $this->assertEquals(1, $callCounter); - } - - public function testResolvingCallbacksAreCalledForConcretesWhenAttachedOnInterface(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingImplementationStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(1, $callCounter); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(2, $callCounter); - } - - public function testResolvingCallbacksAreCalledForConcretesWhenAttachedOnConcretes(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingImplementationStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(1, $callCounter); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(2, $callCounter); - } - - public function testResolvingCallbacksAreCalledForConcretesWithNoBinding(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingImplementationStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(2, $callCounter); - } - - public function testResolvingCallbacksAreCalledForInterFacesWithNoBinding(): void - { - $container = new Container; - - $callCounter = 0; - $container->resolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(2, $callCounter); - } - - public function testAfterResolvingCallbacksAreCalledOnceForImplementation(): void - { - $container = new Container; - - $callCounter = 0; - $container->afterResolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - - $container->make(ResolvingContractStub::class); - $this->assertEquals(2, $callCounter); - } - - public function testBeforeResolvingCallbacksAreCalled(): void - { - // Given a call counter initialized to zero. - $container = new Container; - $callCounter = 0; - - // And a contract/implementation stub binding. - $container->bind(ResolvingContractStub::class, ResolvingImplementationStub::class); - - // When we add a before resolving callback that increment the counter by one. - $container->beforeResolving(ResolvingContractStub::class, function () use (&$callCounter) { - $callCounter++; - }); - - // Then resolving the implementation stub increases the counter by one. - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - - // And resolving the contract stub increases the counter by one. - $container->make(ResolvingContractStub::class); - $this->assertEquals(2, $callCounter); - } - - public function testGlobalBeforeResolvingCallbacksAreCalled(): void - { - // Given a call counter initialized to zero. - $container = new Container; - $callCounter = 0; - - // When we add a global before resolving callback that increment that counter by one. - $container->beforeResolving(function () use (&$callCounter) { - $callCounter++; - }); - - // Then resolving anything increases the counter by one. - $container->make(ResolvingImplementationStub::class); - $this->assertEquals(1, $callCounter); - } -} - -interface ResolvingContractStub -{ - // -} - -class ResolvingImplementationStub implements ResolvingContractStub -{ - // -} - -class ResolvingImplementationStubTwo implements ResolvingContractStub -{ - // -} diff --git a/tests/Container/RewindableGeneratorTest.php b/tests/Container/RewindableGeneratorTest.php deleted file mode 100644 index b65c3498..00000000 --- a/tests/Container/RewindableGeneratorTest.php +++ /dev/null @@ -1,41 +0,0 @@ -assertCount(999, $generator); - } - - public function testCountUsesProvidedValueAsCallback(): void - { - $called = 0; - - $generator = new RewindableGenerator(function () { - yield 'foo'; - }, function () use (&$called) { - $called++; - - return 500; - }); - - // the count callback is called lazily - $this->assertSame(0, $called); - - $this->assertCount(500, $generator); - - count($generator); - - // the count callback is called only once - $this->assertSame(1, $called); - } -} diff --git a/tests/Exception/HandlerTest.php b/tests/Exception/HandlerTest.php index 024c4ab0..4283ad1d 100644 --- a/tests/Exception/HandlerTest.php +++ b/tests/Exception/HandlerTest.php @@ -1,6 +1,6 @@ registerCoreContainerAliases(); - $app->bindShared('hash', function() { return new Illuminate\Hashing\BcryptHasher; }); + $app->singleton('hash', function() { return new Illuminate\Hashing\BcryptHasher; }); $this->assertInstanceOf(Illuminate\Contracts\Hashing\Hasher::class, $app->make(Illuminate\Contracts\Hashing\Hasher::class)); // BC: the pre-migration interface name must still resolve via make()/autowiring; @@ -206,7 +206,7 @@ class ApplicationDeferredSharedServiceProviderStub extends Illuminate\Support\Se protected $defer = true; public function register() { - $this->app->bindShared('foo', function() { + $this->app->singleton('foo', function() { return new StdClass; }); } @@ -247,7 +247,7 @@ class ApplicationMultiProviderStub extends Illuminate\Support\ServiceProvider { protected $defer = true; public function register() { - $this->app->bindShared('foo', function() { return 'foo'; }); - $this->app->bindShared('bar', function($app) { return $app['foo'].'bar'; }); + $this->app->singleton('foo', function() { return 'foo'; }); + $this->app->singleton('bar', function($app) { return $app['foo'].'bar'; }); } } diff --git a/tests/Mail/MailMailerTest.php b/tests/Mail/MailMailerTest.php index 7b6fea15..b06d1e52 100755 --- a/tests/Mail/MailMailerTest.php +++ b/tests/Mail/MailMailerTest.php @@ -213,7 +213,7 @@ public function mail(Message $message): void $this->calledTimes++; } }; - $container['FooMailer'] = $container->share(fn() => $fooMailer); + $container->singleton('FooMailer', fn() => $fooMailer); $mailer->send('foo', ['data'], 'FooMailer'); From fb9072f6d4ffed90e6298416e22155b8d9b1ed92 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 16:52:34 +0700 Subject: [PATCH 08/35] feat(foundation): add bindShared()/share() BC shims to Application (task 4.1, container swap) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit illuminate/container v13 dropped the L4 bindShared()/share() API. Fork src already moved to singleton() (commits 69421972/f1fff797), but third-party & vendor-fork service providers still call them on the app at boot and can't all be patched (tomgrohl/laravel4-php71-encrypter is third-party packagist). Re-expose both as thin BC shims on Foundation\Application (which extends the v13 container): bindShared($a,$c) => singleton($a,$c); share() returns the memoizing closure. Unblocks boot for spatie/laravel-blade-x, laracasts/commander, tomgrohl/laravel4-php71-encrypter, barryvdh/laravel-ide-helper. Transitional — remove at the Foundation swap (task 4.5). Fork suite green (1489). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Foundation/Application.php | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 6e7796b3..784676e1 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -472,6 +472,45 @@ public function make($abstract, $parameters = array(), $raiseEvents = true) return parent::make($abstract, $parameters, $raiseEvents); } + /** + * Register a shared binding. + * + * ponytail: transitional BC shim for the L4 container API removed by + * illuminate/container v13. Kept so third-party/vendor providers that still + * call bindShared()/share() on the app (spatie/laravel-blade-x, + * laracasts/commander, tomgrohl/laravel4-php71-encrypter, barryvdh/laravel-ide-helper) + * keep booting. Remove at the Foundation swap (task 4.5); fork src already uses singleton(). + * + * @param string $abstract + * @param \Closure $closure + * @return void + */ + public function bindShared($abstract, Closure $closure): void + { + $this->singleton($abstract, $closure); + } + + /** + * Wrap a closure so the resolved instance is memoized. BC shim; see bindShared(). + * + * @param \Closure $closure + * @return \Closure + */ + public function share(Closure $closure): Closure + { + return function ($container) use ($closure) + { + static $object; + + if (is_null($object)) + { + $object = $closure($container); + } + + return $object; + }; + } + /** * Determine if the given abstract type has been bound. * From 75997c2ad00ec10e77aa32a8645cfb07796a2d75 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 17:17:17 +0700 Subject: [PATCH 09/35] feat(scc1): swap support+http+session+cache+cookie+encryption+events+filesystem+redis+database+bus+pipeline to illuminate/*:^13 (task 4.1, SCC-1 atomic remainder) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the SCC-1 core cutover: the support-gated cluster swaps to real v13 packages in one window (they all hard-require illuminate/support:^13, which the fork replaced — so none could move piecemeal). Composer: - require illuminate/{support,http,session,cache,cookie,encryption,events,bus, filesystem,redis,database,pipeline}:^13; removed the 10 from replace. - laravel/serializable-closure ^1.2 -> ^2.0.10 (new floor forced by illuminate/database:^13). - Transitive: brick/math, doctrine/inflector, fruitcake/php-cors, guzzle uri-template. Fork src trees are LEFT on disk (not deleted): v13 shadows every path it ships via the longer Illuminate\\ PSR-4 prefix; fork-only files (e.g. Support/Contracts, Support helpers, ClassLoader) still resolve via the Illuminate\ fallback. Same seed mechanism proven in 4.1a-d. Tree evaporation is deferred to task 4.4. Fork code fixes (the entire real break surface — only 3): - Foundation\Application::boot(): v13 ServiceProvider has no default boot() — guard with method_exists and call via the container (keeps boot() method-injection). - View\View: `instanceof MessageProviderInterface` used the L4 fork contract; v13 MessageBag implements Illuminate\Contracts\Support\MessageProvider — aliased to it. - tests/CachedRouting: cache config to v13 shape (cache.stores.file + cache.default). Deleted 63 fork tests for now-vendored components (Database/Cache/Http/Session/Cookie/ Encryption/Events/Filesystem + 4 vendored Support tests) — upstream-tested; the fork must not re-test a dependency (container-swap precedent). KNOWN CONSTRAINT: illuminate/bus is required by illuminate/events but its Dispatcher deep-couples to v13 illuminate/queue (Attributes/*, InteractsWithQueue, Foundation\Bus) which is NOT yet swapped (queue:^13 doesn't co-resolve — needs console). The fork has zero references to Illuminate\Bus\, so Bus\Dispatcher never loads; it stays dormant until the queue swap (task 4.3). L4.2-app uses laracasts/commander, not illuminate/bus. Fork suite green (763; -726 = deleted vendored-component tests). All 12 components verified loading from v13 vendor. composer.lock is gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) --- composer.json | 24 +- src/Illuminate/Foundation/Application.php | 6 +- src/Illuminate/View/View.php | 2 +- tests/Cache/CacheApcStoreTest.php | 70 - tests/Cache/CacheArrayStoreTest.php | 68 - tests/Cache/CacheDatabaseStoreTest.php | 131 -- tests/Cache/CacheFileStoreTest.php | 146 -- tests/Cache/CacheMemcachedConnectorTest.php | 40 - tests/Cache/CacheMemcachedStoreTest.php | 76 - tests/Cache/CacheNullStoreTest.php | 15 - tests/Cache/CacheRedisStoreTest.php | 102 -- tests/Cache/CacheRepositoryTest.php | 94 - tests/Cache/CacheTaggedCacheTest.php | 115 -- .../CachedRouting/RoutingIntegrationTest.php | 5 +- tests/Cookie/CookieTest.php | 139 -- .../DatabaseConnectionFactoryTest.php | 149 -- tests/Database/DatabaseConnectionTest.php | 333 ---- tests/Database/DatabaseConnectorTest.php | 185 -- .../DatabaseEloquentBelongsToManyTest.php | 506 ------ .../DatabaseEloquentBelongsToTest.php | 108 -- .../Database/DatabaseEloquentBuilderTest.php | 629 ------- .../DatabaseEloquentCollectionTest.php | 225 --- .../Database/DatabaseEloquentHasManyTest.php | 115 -- .../DatabaseEloquentHasManyThroughTest.php | 104 -- tests/Database/DatabaseEloquentHasOneTest.php | 139 -- tests/Database/DatabaseEloquentModelTest.php | 1190 ------------- tests/Database/DatabaseEloquentMorphTest.php | 120 -- .../DatabaseEloquentMorphToManyTest.php | 120 -- .../Database/DatabaseEloquentMorphToTest.php | 155 -- tests/Database/DatabaseEloquentPivotTest.php | 104 -- .../Database/DatabaseEloquentRelationTest.php | 118 -- .../Database/DatabaseMigrationCreatorTest.php | 67 - .../DatabaseMigrationInstallCommandTest.php | 33 - .../DatabaseMigrationMakeCommandTest.php | 98 -- .../DatabaseMigrationMigrateCommandTest.php | 123 -- .../DatabaseMigrationRepositoryTest.php | 119 -- .../DatabaseMigrationResetCommandTest.php | 50 - .../DatabaseMigrationRollbackCommandTest.php | 51 - tests/Database/DatabaseMigratorTest.php | 223 --- tests/Database/DatabaseMySqlProcessorTest.php | 22 - .../DatabaseMySqlSchemaGrammarTest.php | 544 ------ .../DatabasePostgresProcessorTest.php | 25 - .../DatabasePostgresSchemaGrammarTest.php | 443 ----- tests/Database/DatabaseProcessorTest.php | 43 - tests/Database/DatabaseQueryBuilderTest.php | 1530 ----------------- .../Database/DatabaseSQLiteProcessorTest.php | 25 - .../DatabaseSQLiteSchemaGrammarTest.php | 419 ----- .../Database/DatabaseSchemaBlueprintTest.php | 61 - tests/Database/DatabaseSchemaBuilderTest.php | 30 - tests/Database/DatabaseSeederTest.php | 52 - .../DatabaseSoftDeletingScopeTest.php | 122 -- .../DatabaseSoftDeletingTraitTest.php | 91 - .../DatabaseSqlServerSchemaGrammarTest.php | 443 ----- .../stubs/EloquentModelNamespacedStub.php | 7 - tests/Encryption/EncrypterTest.php | 42 - tests/Events/EventsDispatcherTest.php | 133 -- tests/Filesystem/FilesystemTest.php | 130 -- tests/Http/HttpJsonResponseTest.php | 34 - tests/Http/HttpRedirectResponseTest.php | 142 -- tests/Http/HttpRequestTest.php | 428 ----- tests/Http/HttpResponseTest.php | 143 -- tests/Session/SessionMiddlewareTest.php | 86 - tests/Session/SessionStoreTest.php | 329 ---- tests/Support/SupportFluentTest.php | 122 -- tests/Support/SupportPluralizerTest.php | 53 - tests/Support/SupportServiceProviderTest.php | 24 - tests/Support/SupportStrTest.php | 199 --- 67 files changed, 22 insertions(+), 11797 deletions(-) delete mode 100755 tests/Cache/CacheApcStoreTest.php delete mode 100755 tests/Cache/CacheArrayStoreTest.php delete mode 100755 tests/Cache/CacheDatabaseStoreTest.php delete mode 100755 tests/Cache/CacheFileStoreTest.php delete mode 100755 tests/Cache/CacheMemcachedConnectorTest.php delete mode 100755 tests/Cache/CacheMemcachedStoreTest.php delete mode 100644 tests/Cache/CacheNullStoreTest.php delete mode 100755 tests/Cache/CacheRedisStoreTest.php delete mode 100755 tests/Cache/CacheRepositoryTest.php delete mode 100644 tests/Cache/CacheTaggedCacheTest.php delete mode 100755 tests/Cookie/CookieTest.php delete mode 100755 tests/Database/DatabaseConnectionFactoryTest.php delete mode 100755 tests/Database/DatabaseConnectionTest.php delete mode 100755 tests/Database/DatabaseConnectorTest.php delete mode 100755 tests/Database/DatabaseEloquentBelongsToManyTest.php delete mode 100755 tests/Database/DatabaseEloquentBelongsToTest.php delete mode 100755 tests/Database/DatabaseEloquentBuilderTest.php delete mode 100755 tests/Database/DatabaseEloquentCollectionTest.php delete mode 100755 tests/Database/DatabaseEloquentHasManyTest.php delete mode 100644 tests/Database/DatabaseEloquentHasManyThroughTest.php delete mode 100755 tests/Database/DatabaseEloquentHasOneTest.php delete mode 100755 tests/Database/DatabaseEloquentModelTest.php delete mode 100755 tests/Database/DatabaseEloquentMorphTest.php delete mode 100644 tests/Database/DatabaseEloquentMorphToManyTest.php delete mode 100644 tests/Database/DatabaseEloquentMorphToTest.php delete mode 100755 tests/Database/DatabaseEloquentPivotTest.php delete mode 100755 tests/Database/DatabaseEloquentRelationTest.php delete mode 100755 tests/Database/DatabaseMigrationCreatorTest.php delete mode 100755 tests/Database/DatabaseMigrationInstallCommandTest.php delete mode 100755 tests/Database/DatabaseMigrationMakeCommandTest.php delete mode 100755 tests/Database/DatabaseMigrationMigrateCommandTest.php delete mode 100755 tests/Database/DatabaseMigrationRepositoryTest.php delete mode 100755 tests/Database/DatabaseMigrationResetCommandTest.php delete mode 100755 tests/Database/DatabaseMigrationRollbackCommandTest.php delete mode 100755 tests/Database/DatabaseMigratorTest.php delete mode 100644 tests/Database/DatabaseMySqlProcessorTest.php delete mode 100755 tests/Database/DatabaseMySqlSchemaGrammarTest.php delete mode 100644 tests/Database/DatabasePostgresProcessorTest.php delete mode 100755 tests/Database/DatabasePostgresSchemaGrammarTest.php delete mode 100755 tests/Database/DatabaseProcessorTest.php delete mode 100755 tests/Database/DatabaseQueryBuilderTest.php delete mode 100644 tests/Database/DatabaseSQLiteProcessorTest.php delete mode 100755 tests/Database/DatabaseSQLiteSchemaGrammarTest.php delete mode 100755 tests/Database/DatabaseSchemaBlueprintTest.php delete mode 100755 tests/Database/DatabaseSchemaBuilderTest.php delete mode 100755 tests/Database/DatabaseSeederTest.php delete mode 100644 tests/Database/DatabaseSoftDeletingScopeTest.php delete mode 100644 tests/Database/DatabaseSoftDeletingTraitTest.php delete mode 100755 tests/Database/DatabaseSqlServerSchemaGrammarTest.php delete mode 100755 tests/Database/stubs/EloquentModelNamespacedStub.php delete mode 100755 tests/Encryption/EncrypterTest.php delete mode 100755 tests/Events/EventsDispatcherTest.php delete mode 100755 tests/Filesystem/FilesystemTest.php delete mode 100644 tests/Http/HttpJsonResponseTest.php delete mode 100755 tests/Http/HttpRedirectResponseTest.php delete mode 100755 tests/Http/HttpRequestTest.php delete mode 100755 tests/Http/HttpResponseTest.php delete mode 100644 tests/Session/SessionMiddlewareTest.php delete mode 100644 tests/Session/SessionStoreTest.php delete mode 100755 tests/Support/SupportFluentTest.php delete mode 100755 tests/Support/SupportPluralizerTest.php delete mode 100755 tests/Support/SupportServiceProviderTest.php delete mode 100755 tests/Support/SupportStrTest.php diff --git a/composer.json b/composer.json index 1b439753..cd10dd3d 100755 --- a/composer.json +++ b/composer.json @@ -13,14 +13,26 @@ "php": ">=8.4.23", "d11wtq/boris": "~1.0", "filp/whoops": "~2.11", + "illuminate/bus": "^13", + "illuminate/cache": "^13", "illuminate/collections": "^13", "illuminate/conditionable": "^13", "illuminate/container": "^13", "illuminate/contracts": "^13", + "illuminate/cookie": "^13", + "illuminate/database": "^13", + "illuminate/encryption": "^13", + "illuminate/events": "^13", + "illuminate/filesystem": "^13", + "illuminate/http": "^13", "illuminate/macroable": "^13", + "illuminate/pipeline": "^13", + "illuminate/redis": "^13", "illuminate/reflection": "^13", + "illuminate/session": "^13", + "illuminate/support": "^13", "ircmaxell/password-compat": "~1.0", - "laravel/serializable-closure": "^1.2", + "laravel/serializable-closure": "^2.0.10", "monolog/monolog": "^3.10", "nesbot/carbon": "^3.8.4", "opis/closure": "~3.6", @@ -44,27 +56,17 @@ }, "replace": { "illuminate/auth": "self.version", - "illuminate/cache": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", - "illuminate/cookie": "self.version", - "illuminate/database": "self.version", - "illuminate/encryption": "self.version", - "illuminate/events": "self.version", "illuminate/exception": "self.version", - "illuminate/filesystem": "self.version", "illuminate/foundation": "self.version", "illuminate/hashing": "self.version", - "illuminate/http": "self.version", "illuminate/html": "self.version", "illuminate/log": "self.version", "illuminate/mail": "self.version", "illuminate/pagination": "self.version", "illuminate/queue": "self.version", - "illuminate/redis": "self.version", "illuminate/routing": "self.version", - "illuminate/session": "self.version", - "illuminate/support": "self.version", "illuminate/translation": "self.version", "illuminate/validation": "self.version", "illuminate/view": "self.version", diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 784676e1..0e1cffd8 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -635,7 +635,11 @@ public function boot() { if ($this->booted) return; - array_walk($this->serviceProviders, function($p) { $p->boot(); }); + array_walk($this->serviceProviders, function($p) { + // v13 ServiceProvider has no default boot(); call only when defined (via + // the container so boot() method-injection keeps working). + if (method_exists($p, 'boot')) $this->call([$p, 'boot']); + }); $this->bootApplication(); } diff --git a/src/Illuminate/View/View.php b/src/Illuminate/View/View.php index 5a33e7e2..e76a4aef 100755 --- a/src/Illuminate/View/View.php +++ b/src/Illuminate/View/View.php @@ -5,7 +5,7 @@ use Illuminate\Contracts\Support\Renderable; use Illuminate\Support\MessageBag; use Illuminate\View\Engines\EngineInterface; -use Illuminate\Support\Contracts\MessageProviderInterface; +use Illuminate\Contracts\Support\MessageProvider as MessageProviderInterface; use Illuminate\Support\Contracts\ArrayableInterface as Arrayable; class View implements ArrayAccess, Renderable diff --git a/tests/Cache/CacheApcStoreTest.php b/tests/Cache/CacheApcStoreTest.php deleted file mode 100755 index ac3d28a2..00000000 --- a/tests/Cache/CacheApcStoreTest.php +++ /dev/null @@ -1,70 +0,0 @@ -getMock(ApcWrapper::class, ['get']); - $apc->expects($this->once())->method('get')->with($this->equalTo('foobar'))->willReturn(null); - $store = new Illuminate\Cache\ApcStore($apc, 'foo'); - $this->assertNull($store->get('bar')); - } - - - public function testAPCValueIsReturned() - { - $apc = $this->getMock(ApcWrapper::class, ['get']); - $apc->expects($this->once())->method('get')->willReturn('bar'); - $store = new Illuminate\Cache\ApcStore($apc); - $this->assertEquals('bar', $store->get('foo')); - } - - - public function testSetMethodProperlyCallsAPC() - { - $apc = $this->getMock(ApcWrapper::class, ['put']); - $apc->expects($this->once())->method('put')->with($this->equalTo('foo'), $this->equalTo('bar'), $this->equalTo(60)); - $store = new Illuminate\Cache\ApcStore($apc); - $store->put('foo', 'bar', 1); - } - - - public function testIncrementMethodProperlyCallsAPC() - { - $apc = $this->getMock(ApcWrapper::class, ['increment']); - $apc->expects($this->once())->method('increment')->with($this->equalTo('foo'), $this->equalTo(5)); - $store = new Illuminate\Cache\ApcStore($apc); - $store->increment('foo', 5); - } - - - public function testDecrementMethodProperlyCallsAPC() - { - $apc = $this->getMock(ApcWrapper::class, ['decrement']); - $apc->expects($this->once())->method('decrement')->with($this->equalTo('foo'), $this->equalTo(5)); - $store = new Illuminate\Cache\ApcStore($apc); - $store->decrement('foo', 5); - } - - - public function testStoreItemForeverProperlyCallsAPC() - { - $apc = $this->getMock(ApcWrapper::class, ['put']); - $apc->expects($this->once())->method('put')->with($this->equalTo('foo'), $this->equalTo('bar'), $this->equalTo(0)); - $store = new Illuminate\Cache\ApcStore($apc); - $store->forever('foo', 'bar'); - } - - - public function testForgetMethodProperlyCallsAPC() - { - $apc = $this->getMock(ApcWrapper::class, ['delete']); - $apc->expects($this->once())->method('delete')->with($this->equalTo('foo')); - $store = new Illuminate\Cache\ApcStore($apc); - $store->forget('foo'); - } - -} diff --git a/tests/Cache/CacheArrayStoreTest.php b/tests/Cache/CacheArrayStoreTest.php deleted file mode 100755 index fd03da61..00000000 --- a/tests/Cache/CacheArrayStoreTest.php +++ /dev/null @@ -1,68 +0,0 @@ -put('foo', 'bar', 10); - $this->assertEquals('bar', $store->get('foo')); - } - - - public function testStoreItemForeverProperlyStoresInArray() - { - $mock = $this->getMock(ArrayStore::class, ['put']); - $mock->expects($this->once())->method('put')->with($this->equalTo('foo'), $this->equalTo('bar'), $this->equalTo(0)); - $mock->forever('foo', 'bar'); - } - - - public function testValuesCanBeIncremented() - { - $store = new ArrayStore; - $store->put('foo', 1, 10); - $store->increment('foo'); - $this->assertEquals(2, $store->get('foo')); - } - - - public function testValuesCanBeDecremented() - { - $store = new ArrayStore; - $store->put('foo', 1, 10); - $store->decrement('foo'); - $this->assertEquals(0, $store->get('foo')); - } - - - public function testItemsCanBeRemoved() - { - $store = new ArrayStore; - $store->put('foo', 'bar', 10); - $store->forget('foo'); - $this->assertNull($store->get('foo')); - } - - - public function testItemsCanBeFlushed() - { - $store = new ArrayStore; - $store->put('foo', 'bar', 10); - $store->put('baz', 'boom', 10); - $store->flush(); - $this->assertNull($store->get('foo')); - $this->assertNull($store->get('baz')); - } - - - public function testCacheKey() - { - $store = new ArrayStore; - $this->assertEquals('', $store->getPrefix()); - } - -} diff --git a/tests/Cache/CacheDatabaseStoreTest.php b/tests/Cache/CacheDatabaseStoreTest.php deleted file mode 100755 index c81b9e43..00000000 --- a/tests/Cache/CacheDatabaseStoreTest.php +++ /dev/null @@ -1,131 +0,0 @@ -getStore(); - $table = m::mock('StdClass'); - $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table); - $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table); - $table->shouldReceive('first')->once()->andReturn(null); - - $this->assertNull($store->get('foo')); - } - - - public function testNullIsReturnedAndItemDeletedWhenItemIsExpired(): void - { - $store = $this->getMock(DatabaseStore::class, ['forget'], $this->getMocks()); - $table = m::mock('StdClass'); - $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table); - $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table); - $table->shouldReceive('first')->once()->andReturn((object) ['expiration' => 1]); - $store->expects($this->once())->method('forget')->with($this->equalTo('foo'))->willReturn(null); - - $this->assertNull($store->get('foo')); - } - - - public function testDecryptedValueIsReturnedWhenItemIsValid(): void - { - $store = $this->getStore(); - $table = m::mock('StdClass'); - $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table); - $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table); - $table->shouldReceive('first')->once()->andReturn((object) ['value' => 'bar', 'expiration' => 999999999999999]); - $store->getEncrypter()->shouldReceive('decrypt')->once()->with('bar')->andReturn('bar'); - - $this->assertEquals('bar', $store->get('foo')); - } - - - public function testEncryptedValueIsInsertedWhenNoExceptionsAreThrown(): void - { - $store = $this->getMock(DatabaseStore::class, ['getTime'], $this->getMocks()); - $table = m::mock('StdClass'); - $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table); - $store->getEncrypter()->shouldReceive('encrypt')->once()->with('bar')->andReturn('bar'); - $store->expects($this->once())->method('getTime')->willReturn(1); - $table->shouldReceive('insert')->once()->with(['key' => 'prefixfoo', 'value' => 'bar', 'expiration' => 61]); - - $store->put('foo', 'bar', 1); - } - - - public function testEncryptedValueIsUpdatedWhenInsertThrowsException(): void - { - $store = $this->getMock(DatabaseStore::class, ['getTime'], $this->getMocks()); - $table = m::mock('StdClass'); - $store->getConnection()->shouldReceive('table')->with('table')->andReturn($table); - $store->getEncrypter()->shouldReceive('encrypt')->once()->with('bar')->andReturn('bar'); - $store->expects($this->once())->method('getTime')->willReturn(1); - $table->shouldReceive('insert')->once()->with(['key' => 'prefixfoo', 'value' => 'bar', 'expiration' => 61])->andReturnUsing(function(): never - { - throw new Exception; - }); - $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table); - $table->shouldReceive('update')->once()->with(['value' => 'bar', 'expiration' => 61]); - - $store->put('foo', 'bar', 1); - } - - - public function testForeverCallsStoreItemWithReallyLongTime(): void - { - $store = $this->getMock(DatabaseStore::class, ['put'], $this->getMocks()); - $store->expects($this->once())->method('put')->with($this->equalTo('foo'), $this->equalTo('bar'), $this->equalTo(5256000)); - $store->forever('foo', 'bar'); - } - - - public function testItemsMayBeRemovedFromCache(): void - { - $store = $this->getStore(); - $table = m::mock('StdClass'); - $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table); - $table->shouldReceive('where')->once()->with('key', '=', 'prefixfoo')->andReturn($table); - $table->shouldReceive('delete')->once(); - - $store->forget('foo'); - } - - - public function testItemsMayBeFlushedFromCache(): void - { - $store = $this->getStore(); - $table = m::mock('StdClass'); - $store->getConnection()->shouldReceive('table')->once()->with('table')->andReturn($table); - $table->shouldReceive('delete')->once(); - - $store->flush(); - } - - - protected function getStore(): DatabaseStore - { - return new DatabaseStore(m::mock(Connection::class), m::mock( - Encrypter::class - ), 'table', 'prefix'); - } - - - protected function getMocks(): array - { - return [m::mock(Connection::class), m::mock(Encrypter::class), 'table', 'prefix']; - } - -} diff --git a/tests/Cache/CacheFileStoreTest.php b/tests/Cache/CacheFileStoreTest.php deleted file mode 100755 index 9b8f96bd..00000000 --- a/tests/Cache/CacheFileStoreTest.php +++ /dev/null @@ -1,146 +0,0 @@ -mockFilesystem(); - $files->expects($this->once())->method('exists')->willReturn(false); - $store = new FileStore($files, __DIR__); - $value = $store->get('foo'); - $this->assertNull($value); - } - - - public function testPutCreatesMissingDirectories() - { - $files = $this->mockFilesystem(); - $md5 = md5('foo'); - $full_dir = __DIR__.'/'.substr($md5, 0, 2).'/'.substr($md5, 2, 2); - $files->expects($this->once())->method('makeDirectory')->with($this->equalTo($full_dir), $this->equalTo(0777), $this->equalTo(true)); - $files->expects($this->once())->method('put')->with($this->equalTo($full_dir.'/'.$md5)); - $store = new FileStore($files, __DIR__); - $store->put('foo', '0000000000', 0); - } - - - public function testExpiredItemsReturnNull() - { - $files = $this->mockFilesystem(); - $files->expects($this->once())->method('exists')->willReturn(true); - $contents = '0000000000'; - $files->expects($this->once())->method('get')->willReturn($contents); - $store = $this->getMock(FileStore::class, ['forget'], [$files, __DIR__]); - $store->expects($this->once())->method('forget'); - $value = $store->get('foo'); - $this->assertNull($value); - } - - - public function testValidItemReturnsContents() - { - $files = $this->mockFilesystem(); - $files->expects($this->once())->method('exists')->willReturn(true); - $contents = '9999999999'.serialize('Hello World'); - $files->expects($this->once())->method('get')->willReturn($contents); - $store = new FileStore($files, __DIR__); - $this->assertEquals('Hello World', $store->get('foo')); - } - - - public function testStoreItemProperlyStoresValues() - { - $files = $this->mockFilesystem(); - $store = $this->getMock(FileStore::class, ['expiration'], [$files, __DIR__]); - $store->expects($this->once())->method('expiration')->with($this->equalTo(10))->willReturn(1111111111); - $contents = '1111111111'.serialize('Hello World'); - $md5 = md5('foo'); - $cache_dir = substr($md5, 0, 2).'/'.substr($md5, 2, 2); - $files->expects($this->once())->method('put')->with($this->equalTo(__DIR__.'/'.$cache_dir.'/'.$md5), $this->equalTo($contents)); - $store->put('foo', 'Hello World', 10); - } - - - public function testForeversAreStoredWithHighTimestamp() - { - $files = $this->mockFilesystem(); - $contents = '9999999999'.serialize('Hello World'); - $md5 = md5('foo'); - $cache_dir = substr($md5, 0, 2).'/'.substr($md5, 2, 2); - $files->expects($this->once())->method('put')->with($this->equalTo(__DIR__.'/'.$cache_dir.'/'.$md5), $this->equalTo($contents)); - $store = new FileStore($files, __DIR__); - $store->forever('foo', 'Hello World', 10); - } - - - public function testRemoveDeletesFileDoesntExist() - { - $files = $this->mockFilesystem(); - $md5 = md5('foobull'); - $cache_dir = substr($md5, 0, 2).'/'.substr($md5, 2, 2); - $files->expects($this->once())->method('exists')->with($this->equalTo(__DIR__.'/'.$cache_dir.'/'.$md5))->willReturn( - false - ); - $store = new FileStore($files, __DIR__); - $store->forget('foobull'); - } - - - public function testRemoveDeletesFile() - { - $files = $this->mockFilesystem(); - $md5 = md5('foobar'); - $cache_dir = substr($md5, 0, 2).'/'.substr($md5, 2, 2); - $store = new FileStore($files, __DIR__); - $store->put('foobar', 'Hello Baby', 10); - $files->expects($this->once())->method('exists')->with($this->equalTo(__DIR__.'/'.$cache_dir.'/'.$md5))->willReturn( - true - ); - $files->expects($this->once())->method('delete')->with($this->equalTo(__DIR__.'/'.$cache_dir.'/'.$md5)); - $store->forget('foobar'); - } - - - public function testFlushCleansDirectory() - { - $files = $this->mockFilesystem(); - $files->expects($this->once())->method('isDirectory')->with($this->equalTo(__DIR__))->willReturn(true); - $files->expects($this->once())->method('directories')->with($this->equalTo(__DIR__))->willReturn(['foo']); - $files->expects($this->once())->method('deleteDirectory')->with($this->equalTo('foo')); - - $store = new FileStore($files, __DIR__); - $store->flush(); - } - - - public function testFlushIgnoreNonExistingDirectory() - { - $files = $this->mockFilesystem(); - $files->expects($this->once())->method('isDirectory')->with($this->equalTo(__DIR__ . '--wrong'))->willReturn( - false - ); - - $store = new FileStore($files, __DIR__ . '--wrong'); - $store->flush(); - } - - - protected function mockFilesystem() - { - return $this->getMock(Filesystem::class, [ - 'get', - 'put', - 'exists', - 'delete', - 'directories', - 'isDirectory', - 'makeDirectory', - 'deleteDirectory' - ]); - } - -} diff --git a/tests/Cache/CacheMemcachedConnectorTest.php b/tests/Cache/CacheMemcachedConnectorTest.php deleted file mode 100755 index 0feac57f..00000000 --- a/tests/Cache/CacheMemcachedConnectorTest.php +++ /dev/null @@ -1,40 +0,0 @@ -getMock(MemcachedConnector::class, ['getMemcached']); - $memcached = m::mock('stdClass'); - $memcached->shouldReceive('addServer')->once()->with('localhost', 11211, 100); - $memcached->shouldReceive('getVersion')->once()->andReturn(true); - $connector->expects($this->once())->method('getMemcached')->willReturn($memcached); - $result = $connector->connect([['host' => 'localhost', 'port' => 11211, 'weight' => 100]]); - - $this->assertSame($result, $memcached); - } - - - public function testExceptionThrownOnBadConnection() - { - $this->expectException(RuntimeException::class); - $connector = $this->getMock(MemcachedConnector::class, ['getMemcached']); - $memcached = m::mock('stdClass'); - $memcached->shouldReceive('addServer')->once()->with('localhost', 11211, 100); - $memcached->shouldReceive('getVersion')->once()->andReturn(false); - $connector->expects($this->once())->method('getMemcached')->willReturn($memcached); - $result = $connector->connect([['host' => 'localhost', 'port' => 11211, 'weight' => 100]]); - } - -} diff --git a/tests/Cache/CacheMemcachedStoreTest.php b/tests/Cache/CacheMemcachedStoreTest.php deleted file mode 100755 index edb793a2..00000000 --- a/tests/Cache/CacheMemcachedStoreTest.php +++ /dev/null @@ -1,76 +0,0 @@ -markTestSkipped("We dont use Memcached"); - } - - public function testGetReturnsNullWhenNotFound() - { - $memcache = $this->getMock(stdClass::class, ['get', 'getResultCode']); - $memcache->expects($this->once())->method('get')->with($this->equalTo('foo:bar'))->willReturn(null); - $memcache->expects($this->once())->method('getResultCode')->willReturn(1); - $store = new Illuminate\Cache\MemcachedStore($memcache, 'foo'); - $this->assertNull($store->get('bar')); - } - - - public function testMemcacheValueIsReturned() - { - $memcache = $this->getMock(stdClass::class, ['get', 'getResultCode']); - $memcache->expects($this->once())->method('get')->willReturn('bar'); - $memcache->expects($this->once())->method('getResultCode')->willReturn(0); - $store = new Illuminate\Cache\MemcachedStore($memcache); - $this->assertEquals('bar', $store->get('foo')); - } - - - public function testSetMethodProperlyCallsMemcache() - { - $memcache = $this->getMock(Memcached::class, ['set']); - $memcache->expects($this->once())->method('set')->with($this->equalTo('foo'), $this->equalTo('bar'), $this->equalTo(60)); - $store = new Illuminate\Cache\MemcachedStore($memcache); - $store->put('foo', 'bar', 1); - } - - - public function testIncrementMethodProperlyCallsMemcache() - { - $memcache = $this->getMock(Memcached::class, ['increment']); - $memcache->expects($this->once())->method('increment')->with($this->equalTo('foo'), $this->equalTo(5)); - $store = new Illuminate\Cache\MemcachedStore($memcache); - $store->increment('foo', 5); - } - - - public function testDecrementMethodProperlyCallsMemcache() - { - $memcache = $this->getMock(Memcached::class, ['decrement']); - $memcache->expects($this->once())->method('decrement')->with($this->equalTo('foo'), $this->equalTo(5)); - $store = new Illuminate\Cache\MemcachedStore($memcache); - $store->decrement('foo', 5); - } - - - public function testStoreItemForeverProperlyCallsMemcached() - { - $memcache = $this->getMock(Memcached::class, ['set']); - $memcache->expects($this->once())->method('set')->with($this->equalTo('foo'), $this->equalTo('bar'), $this->equalTo(0)); - $store = new Illuminate\Cache\MemcachedStore($memcache); - $store->forever('foo', 'bar'); - } - - - public function testForgetMethodProperlyCallsMemcache() - { - $memcache = $this->getMock(Memcached::class, ['delete']); - $memcache->expects($this->once())->method('delete')->with($this->equalTo('foo')); - $store = new Illuminate\Cache\MemcachedStore($memcache); - $store->forget('foo'); - } - -} diff --git a/tests/Cache/CacheNullStoreTest.php b/tests/Cache/CacheNullStoreTest.php deleted file mode 100644 index f0f09cb4..00000000 --- a/tests/Cache/CacheNullStoreTest.php +++ /dev/null @@ -1,15 +0,0 @@ -put('foo', 'bar', 10); - $this->assertNull($store->get('foo')); - } - -} diff --git a/tests/Cache/CacheRedisStoreTest.php b/tests/Cache/CacheRedisStoreTest.php deleted file mode 100755 index 851f5702..00000000 --- a/tests/Cache/CacheRedisStoreTest.php +++ /dev/null @@ -1,102 +0,0 @@ -getRedis(); - $redis->getRedis()->shouldReceive('connection')->once()->with('default')->andReturn($redis->getRedis()); - $redis->getRedis()->shouldReceive('get')->once()->with('prefix:foo')->andReturn(null); - $this->assertNull($redis->get('foo')); - } - - - public function testRedisValueIsReturned() - { - $redis = $this->getRedis(); - $redis->getRedis()->shouldReceive('connection')->once()->with('default')->andReturn($redis->getRedis()); - $redis->getRedis()->shouldReceive('get')->once()->with('prefix:foo')->andReturn(serialize('foo')); - $this->assertEquals('foo', $redis->get('foo')); - } - - - public function testRedisValueIsReturnedForNumerics() - { - $redis = $this->getRedis(); - $redis->getRedis()->shouldReceive('connection')->once()->with('default')->andReturn($redis->getRedis()); - $redis->getRedis()->shouldReceive('get')->once()->with('prefix:foo')->andReturn(1); - $this->assertEquals(1, $redis->get('foo')); - } - - - public function testSetMethodProperlyCallsRedis() - { - $redis = $this->getRedis(); - $redis->getRedis()->shouldReceive('connection')->once()->with('default')->andReturn($redis->getRedis()); - $redis->getRedis()->shouldReceive('setex')->once()->with('prefix:foo', 60 * 60, serialize('foo')); - $redis->put('foo', 'foo', 60); - } - - - public function testSetMethodProperlyCallsRedisForNumerics() - { - $redis = $this->getRedis(); - $redis->getRedis()->shouldReceive('connection')->once()->with('default')->andReturn($redis->getRedis()); - $redis->getRedis()->shouldReceive('setex')->once()->with('prefix:foo', 60 * 60, 1); - $redis->put('foo', 1, 60); - } - - - public function testIncrementMethodProperlyCallsRedis() - { - $redis = $this->getRedis(); - $redis->getRedis()->shouldReceive('connection')->once()->with('default')->andReturn($redis->getRedis()); - $redis->getRedis()->shouldReceive('incrby')->once()->with('prefix:foo', 5); - $redis->increment('foo', 5); - } - - - public function testDecrementMethodProperlyCallsRedis() - { - $redis = $this->getRedis(); - $redis->getRedis()->shouldReceive('connection')->once()->with('default')->andReturn($redis->getRedis()); - $redis->getRedis()->shouldReceive('decrby')->once()->with('prefix:foo', 5); - $redis->decrement('foo', 5); - } - - - public function testStoreItemForeverProperlyCallsRedis() - { - $redis = $this->getRedis(); - $redis->getRedis()->shouldReceive('connection')->once()->with('default')->andReturn($redis->getRedis()); - $redis->getRedis()->shouldReceive('set')->once()->with('prefix:foo', serialize('foo')); - $redis->forever('foo', 'foo', 60); - } - - - public function testForgetMethodProperlyCallsRedis() - { - $redis = $this->getRedis(); - $redis->getRedis()->shouldReceive('connection')->once()->with('default')->andReturn($redis->getRedis()); - $redis->getRedis()->shouldReceive('del')->once()->with('prefix:foo'); - $redis->forget('foo'); - } - - - protected function getRedis() - { - return new Illuminate\Cache\RedisStore(m::mock(Database::class), 'prefix'); - } - -} diff --git a/tests/Cache/CacheRepositoryTest.php b/tests/Cache/CacheRepositoryTest.php deleted file mode 100755 index 67453db7..00000000 --- a/tests/Cache/CacheRepositoryTest.php +++ /dev/null @@ -1,94 +0,0 @@ -getRepository(); - $repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn('bar'); - $this->assertEquals('bar', $repo->get('foo')); - } - - - public function testDefaultValueIsReturned() - { - $repo = $this->getRepository(); - $repo->getStore()->shouldReceive('get')->andReturn(null); - $this->assertEquals('bar', $repo->get('foo', 'bar')); - $this->assertEquals('baz', $repo->get('boom', function() { return 'baz'; })); - } - - - public function testSettingDefaultCacheTime() - { - $repo = $this->getRepository(); - $repo->setDefaultCacheTime(10); - $this->assertEquals(10, $repo->getDefaultCacheTime()); - } - - - public function testHasMethod() - { - $repo = $this->getRepository(); - $repo->getStore()->shouldReceive('get')->once()->with('foo')->andReturn(null); - $repo->getStore()->shouldReceive('get')->once()->with('bar')->andReturn('bar'); - - $this->assertTrue($repo->has('bar')); - $this->assertFalse($repo->has('foo')); - } - - - public function testRememberMethodCallsPutAndReturnsDefault() - { - $repo = $this->getRepository(); - $repo->getStore()->shouldReceive('get')->andReturn(null); - $repo->getStore()->shouldReceive('put')->once()->with('foo', 'bar', m::type('int')); - $result = $repo->remember('foo', Carbon::now()->addMinutes(10), function() { return 'bar'; }); - $this->assertEquals('bar', $result); - } - - - public function testPutAcceptsDateIntervalTtl() - { - $repo = $this->getRepository(); - $repo->getStore()->shouldReceive('put')->once()->with('foo', 'bar', m::type('int')); - $repo->put('foo', 'bar', new DateInterval('PT10M')); - } - - - public function testRememberForeverMethodCallsForeverAndReturnsDefault() - { - $repo = $this->getRepository(); - $repo->getStore()->shouldReceive('get')->andReturn(null); - $repo->getStore()->shouldReceive('forever')->once()->with('foo', 'bar'); - $result = $repo->rememberForever('foo', function() { return 'bar'; }); - $this->assertEquals('bar', $result); - } - - - public function testRegisterMacroWithNonStaticCall() - { - $repo = $this->getRepository(); - $repo::macro(__CLASS__, function() { return 'Taylor'; }); - $this->assertEquals($repo->{__CLASS__}(), 'Taylor'); - } - - - protected function getRepository() - { - return new Illuminate\Cache\Repository(m::mock(StoreInterface::class)); - } - -} diff --git a/tests/Cache/CacheTaggedCacheTest.php b/tests/Cache/CacheTaggedCacheTest.php deleted file mode 100644 index f1fb4199..00000000 --- a/tests/Cache/CacheTaggedCacheTest.php +++ /dev/null @@ -1,115 +0,0 @@ -section('bop')->put('foo', 'bar', Carbon::now()->addMinutes(10)); - $store->section('zap')->put('baz', 'boom', Carbon::now()->addMinutes(10)); - $store->section('bop')->flush(); - $this->assertNull($store->section('bop')->get('foo')); - $this->assertEquals('boom', $store->section('zap')->get('baz')); - } - - - public function testCacheCanBeSavedWithMultipleTags() - { - $store = new ArrayStore; - $tags = ['bop', 'zap']; - $store->tags($tags)->put('foo', 'bar', Carbon::now()->addMinutes(10)); - $this->assertEquals('bar', $store->tags($tags)->get('foo')); - } - - - public function testCacheCanBeSetWithDatetimeArgument() - { - $store = new ArrayStore; - $tags = ['bop', 'zap']; - $duration = new DateTime(); - $duration->add(new DateInterval("PT10M")); - $store->tags($tags)->put('foo', 'bar', $duration); - $this->assertEquals('bar', $store->tags($tags)->get('foo')); - } - - - public function testCacheSavedWithMultipleTagsCanBeFlushed() - { - $store = new ArrayStore; - $tags1 = ['bop', 'zap']; - $store->tags($tags1)->put('foo', 'bar', Carbon::now()->addMinutes(10)); - $tags2 = ['bam', 'pow']; - $store->tags($tags2)->put('foo', 'bar', Carbon::now()->addMinutes(10)); - $store->tags('zap')->flush(); - $this->assertNull($store->tags($tags1)->get('foo')); - $this->assertEquals('bar', $store->tags($tags2)->get('foo')); - } - - - public function testTagsWithStringArgument() - { - $store = new ArrayStore; - $store->tags('bop')->put('foo', 'bar', Carbon::now()->addMinutes(10)); - $this->assertEquals('bar', $store->tags('bop')->get('foo')); - } - - - public function testTagsCacheForever() - { - $store = new ArrayStore; - $tags = ['bop', 'zap']; - $store->tags($tags)->forever('foo', 'bar'); - $this->assertEquals('bar', $store->tags($tags)->get('foo')); - } - - - public function testRedisCacheTagsPushForeverKeysCorrectly() - { - $store = m::mock(StoreInterface::class); - $tagSet = m::mock(TagSet::class, [$store, ['foo', 'bar']]); - $tagSet->shouldReceive('getNamespace')->andReturn('foo|bar'); - $redis = new Illuminate\Cache\RedisTaggedCache($store, $tagSet); - $store->shouldReceive('getPrefix')->andReturn('prefix:'); - $store->shouldReceive('connection')->andReturn($conn = m::mock('StdClass')); - $conn->shouldReceive('lpush')->once()->with('prefix:foo:forever', 'prefix:'.sha1('foo|bar').':key1'); - $conn->shouldReceive('lpush')->once()->with('prefix:bar:forever', 'prefix:'.sha1('foo|bar').':key1'); - $store->shouldReceive('forever')->with(sha1('foo|bar').':key1', 'key1:value'); - - $redis->forever('key1', 'key1:value'); - } - - - public function testRedisCacheForeverTagsCanBeFlushed() - { - $store = m::mock(StoreInterface::class); - $tagSet = m::mock(TagSet::class, [$store, ['foo', 'bar']]); - $tagSet->shouldReceive('getNamespace')->andReturn('foo|bar'); - $redis = new Illuminate\Cache\RedisTaggedCache($store, $tagSet); - $store->shouldReceive('getPrefix')->andReturn('prefix:'); - $store->shouldReceive('connection')->andReturn($conn = m::mock('StdClass')); - $conn->shouldReceive('lrange')->once()->with('prefix:foo:forever', 0, -1)->andReturn(['key1', 'key2']); - $conn->shouldReceive('lrange')->once()->with('prefix:bar:forever', 0, -1)->andReturn(['key3']); - $conn->shouldReceive('del')->once()->with('key1', 'key2'); - $conn->shouldReceive('del')->once()->with('key3'); - $conn->shouldReceive('del')->once()->with('prefix:foo:forever'); - $conn->shouldReceive('del')->once()->with('prefix:bar:forever'); - $tagSet->shouldReceive('reset')->once(); - - $redis->flush(); - } - -} diff --git a/tests/CachedRouting/RoutingIntegrationTest.php b/tests/CachedRouting/RoutingIntegrationTest.php index c27b700d..7776713f 100755 --- a/tests/CachedRouting/RoutingIntegrationTest.php +++ b/tests/CachedRouting/RoutingIntegrationTest.php @@ -95,8 +95,9 @@ protected function refreshApplication(): void $this->app['files'] = new Filesystem; $this->app['cache'] = new CacheManager($this->app); - $this->app['config']['cache.driver'] = 'file'; - $this->app['config']['cache.path'] = self::$cachePath = sys_get_temp_dir() . '/l42x-route-cache-' . uniqid(); + self::$cachePath = sys_get_temp_dir() . '/l42x-route-cache-' . uniqid(); + $this->app['config']['cache.default'] = 'file'; + $this->app['config']['cache.stores.file'] = ['driver' => 'file', 'path' => self::$cachePath]; $this->app['session'] = new SessionManager($this->app); $this->app['config']['session.driver'] = 'array'; diff --git a/tests/Cookie/CookieTest.php b/tests/Cookie/CookieTest.php deleted file mode 100755 index c4128e37..00000000 --- a/tests/Cookie/CookieTest.php +++ /dev/null @@ -1,139 +0,0 @@ -getCreator(); - $cookie->setDefaultPathAndDomain('foo', 'bar'); - $c = $cookie->make('color', 'blue', 10, '/path', '/domain', true, false); - $this->assertEquals('blue', $c->getValue()); - $this->assertFalse($c->isHttpOnly()); - $this->assertTrue($c->isSecure()); - $this->assertEquals('/domain', $c->getDomain()); - $this->assertEquals('/path', $c->getPath()); - - $c2 = $cookie->forever('color', 'blue', '/path', '/domain', true, false); - $this->assertEquals('blue', $c2->getValue()); - $this->assertFalse($c2->isHttpOnly()); - $this->assertTrue($c2->isSecure()); - $this->assertEquals('/domain', $c2->getDomain()); - $this->assertEquals('/path', $c2->getPath()); - - $c3 = $cookie->forget('color'); - $this->assertNull($c3->getValue()); - $this->assertTrue($c3->getExpiresTime() < time()); - } - - - public function testCookiesAreCreatedWithProperOptionsUsingDefaultPathAndDomain() - { - $cookie = $this->getCreator(); - $cookie->setDefaultPathAndDomain('/path', '/domain'); - $c = $cookie->make('color', 'blue', 10, null, null, true, false); - $this->assertEquals('blue', $c->getValue()); - $this->assertFalse($c->isHttpOnly()); - $this->assertTrue($c->isSecure()); - $this->assertEquals('/domain', $c->getDomain()); - $this->assertEquals('/path', $c->getPath()); - } - - - public function testSameSiteAndRawWidening() - { - $cookie = $this->getCreator(); - - // behavior-preserving default: L4.2/Symfony effective SameSite = lax - $this->assertSame('lax', $cookie->make('a', 'b')->getSameSite()); - $this->assertFalse($cookie->make('a', 'b')->isRaw()); - - // per-cookie overrides via the widened signature - $c = $cookie->make('a', 'b', 0, null, null, null, true, true, 'strict'); - $this->assertSame('strict', $c->getSameSite()); - $this->assertTrue($c->isRaw()); - } - - - public function testQueuedCookies() - { - $cookie = $this->getCreator(); - $this->assertEmpty($cookie->getQueuedCookies()); - $this->assertFalse($cookie->hasQueued('foo')); - $cookie->queue($cookie->make('foo','bar')); - $this->assertTrue($cookie->hasQueued('foo')); - $this->assertInstanceOf(Cookie::class, $cookie->queued('foo')); - $cookie->queue('qu','ux'); - $this->assertTrue($cookie->hasQueued('qu')); - $this->assertInstanceOf(Cookie::class, $cookie->queued('qu')); - $this->assertCount(2, $cookie->getQueuedCookies()); - } - - - public function testUnqueue() - { - $cookie = $this->getCreator(); - $cookie->queue($cookie->make('foo','bar')); - $this->assertTrue($cookie->hasQueued('foo')); - $cookie->unqueue('foo'); - $this->assertEmpty($cookie->getQueuedCookies()); - $this->assertFalse($cookie->hasQueued('foo')); - } - - - public function testPathAwareQueuedCookies() - { - $cookie = $this->getCreator(); - $cookie->queue($cookie->make('foo', 'a', 0, '/a')); - $cookie->queue($cookie->make('foo', 'b', 0, '/b')); - - $this->assertCount(2, $cookie->getQueuedCookies()); - $this->assertSame('a', $cookie->queued('foo', null, '/a')->getValue()); - $this->assertSame('b', $cookie->queued('foo', null, '/b')->getValue()); - $this->assertSame('b', $cookie->queued('foo')->getValue()); - - $cookie->unqueue('foo', '/a'); - $this->assertNull($cookie->queued('foo', null, '/a')); - $this->assertSame('b', $cookie->queued('foo', null, '/b')->getValue()); - $this->assertCount(1, $cookie->getQueuedCookies()); - - $cookie->flushQueuedCookies(); - $this->assertEmpty($cookie->getQueuedCookies()); - } - - - public function testExpireQueuesForgetCookie() - { - $cookie = $this->getCreator(); - $cookie->expire('foo'); - - $this->assertTrue($cookie->hasQueued('foo')); - $queued = $cookie->queued('foo'); - $this->assertInstanceOf(Cookie::class, $queued); - $this->assertTrue($queued->getExpiresTime() < time()); - } - - - public function getCreator() - { - return new CookieJar(Request::create('/foo', 'GET'), [ - 'path' => '/path', - 'domain' => '/domain', - 'secure' => true, - 'httpOnly' => false, - ]); - } - -} diff --git a/tests/Database/DatabaseConnectionFactoryTest.php b/tests/Database/DatabaseConnectionFactoryTest.php deleted file mode 100755 index bf26ee10..00000000 --- a/tests/Database/DatabaseConnectionFactoryTest.php +++ /dev/null @@ -1,149 +0,0 @@ -getMock( - ConnectionFactory::class, - ['createConnector', 'createConnection'], - [$container = m::mock(Container::class)] - ); - $container->shouldReceive('bound')->andReturn(false); - $connector = m::mock('stdClass'); - $config = ['driver' => 'mysql', 'prefix' => 'prefix', 'database' => 'database', 'name' => 'foo']; - $pdo = new DatabaseConnectionFactoryPDOStub; - $connector->shouldReceive('connect')->once()->with($config)->andReturn($pdo); - $factory->expects($this->once())->method('createConnector')->with($config)->willReturn($connector); - $mockConnection = m::mock('stdClass'); - $passedConfig = array_merge($config, ['name' => 'foo']); - $factory->expects($this->once())->method('createConnection')->with($this->equalTo('mysql'), $this->equalTo($pdo), $this->equalTo('database'), $this->equalTo('prefix'), $this->equalTo($passedConfig))->willReturn( - $mockConnection - ); - $connection = $factory->make($config, 'foo'); - - $this->assertEquals($mockConnection, $connection); - } - - - public function testMakeCallsCreateConnectionForReadWrite() - { - $factory = $this->getMock(ConnectionFactory::class, ['createConnector', 'createConnection'], [ - $container = m::mock( - Container::class - ) - ]); - $container->shouldReceive('bound')->andReturn(false); - $connector = m::mock('stdClass'); - $config = [ - 'read' => ['database' => 'database'], - 'write' => ['database' => 'database'], - 'driver' => 'mysql', 'prefix' => 'prefix', 'name' => 'foo', - ]; - $expect = $config; - unset($expect['read']); - unset($expect['write']); - $expect['database'] = 'database'; - $pdo = new DatabaseConnectionFactoryPDOStub; - $connector->shouldReceive('connect')->twice()->with($expect)->andReturn($pdo); - $factory->expects($this->exactly(2))->method('createConnector')->with($expect)->willReturn($connector); - $mockConnection = m::mock('stdClass'); - $mockConnection->shouldReceive('setReadPdo')->once()->andReturn($mockConnection); - $passedConfig = array_merge($expect, ['name' => 'foo']); - $factory->expects($this->once())->method('createConnection')->with($this->equalTo('mysql'), $this->equalTo($pdo), $this->equalTo('database'), $this->equalTo('prefix'), $this->equalTo($passedConfig))->willReturn( - $mockConnection - ); - $connection = $factory->make($config, 'foo'); - - $this->assertEquals($mockConnection, $connection); - } - - - public function testMakeCanCallTheContainer() - { - $factory = $this->getMock(ConnectionFactory::class, ['createConnector'], [ - $container = m::mock( - Container::class - ) - ]); - $container->shouldReceive('bound')->andReturn(true); - $connector = m::mock('stdClass'); - $config = ['driver' => 'mysql', 'prefix' => 'prefix', 'database' => 'database', 'name' => 'foo']; - $pdo = new DatabaseConnectionFactoryPDOStub; - $connector->shouldReceive('connect')->once()->with($config)->andReturn($pdo); - $passedConfig = array_merge($config, ['name' => 'foo']); - $factory->expects($this->once())->method('createConnector')->with($config)->willReturn($connector); - $container->shouldReceive('make')->once()->with('db.connection.mysql', [$pdo, 'database', 'prefix', $passedConfig] - )->andReturn('foo'); - $connection = $factory->make($config, 'foo'); - - $this->assertEquals('foo', $connection); - } - - - public function testProperInstancesAreReturnedForProperDrivers() - { - $factory = new Illuminate\Database\Connectors\ConnectionFactory($container = m::mock( - Container::class - )); - $container->shouldReceive('bound')->andReturn(false); - $this->assertInstanceOf(MySqlConnector::class, $factory->createConnector(['driver' => 'mysql'])); - $this->assertInstanceOf(PostgresConnector::class, $factory->createConnector(['driver' => 'pgsql'])); - $this->assertInstanceOf(SQLiteConnector::class, $factory->createConnector(['driver' => 'sqlite'])); - $this->assertInstanceOf(SqlServerConnector::class, $factory->createConnector(['driver' => 'sqlsrv'])); - } - - - public function testIfDriverIsntSetExceptionIsThrown() - { - $this->expectException(InvalidArgumentException::class); - $factory = new Illuminate\Database\Connectors\ConnectionFactory( - $container = m::mock(Container::class) - ); - $factory->createConnector(['foo']); - } - - - public function testExceptionIsThrownOnUnsupportedDriver() - { - $this->expectException(InvalidArgumentException::class); - $factory = new Illuminate\Database\Connectors\ConnectionFactory( - $container = m::mock(Container::class) - ); - $container->shouldReceive('bound')->once()->andReturn(false); - $factory->createConnector(['driver' => 'foo']); - } - - - public function testCustomConnectorsCanBeResolvedViaContainer() - { - $factory = new Illuminate\Database\Connectors\ConnectionFactory($container = m::mock( - Container::class - )); - $container->shouldReceive('bound')->once()->with('db.connector.foo')->andReturn(true); - $container->shouldReceive('make')->once()->with('db.connector.foo')->andReturn('connector'); - - $this->assertEquals('connector', $factory->createConnector(['driver' => 'foo'])); - } - -} diff --git a/tests/Database/DatabaseConnectionTest.php b/tests/Database/DatabaseConnectionTest.php deleted file mode 100755 index 42cca220..00000000 --- a/tests/Database/DatabaseConnectionTest.php +++ /dev/null @@ -1,333 +0,0 @@ -getMockConnection(); - $mock = m::mock(stdClass::class); - $connection->expects($this->once())->method('getDefaultQueryGrammar')->willReturn($mock); - $connection->useDefaultQueryGrammar(); - $this->assertEquals($mock, $connection->getQueryGrammar()); - } - - - public function testSettingDefaultCallsGetDefaultPostProcessor() - { - $connection = $this->getMockConnection(); - $mock = m::mock(stdClass::class); - $connection->expects($this->once())->method('getDefaultPostProcessor')->willReturn($mock); - $connection->useDefaultPostProcessor(); - $this->assertEquals($mock, $connection->getPostProcessor()); - } - - - public function testSelectOneCallsSelectAndReturnsSingleResult() - { - $connection = $this->getMockConnection(['select']); - $connection->expects($this->once())->method('select')->with('foo', ['bar' => 'baz'])->willReturn( - ['foo'] - ); - $this->assertEquals('foo', $connection->selectOne('foo', ['bar' => 'baz'])); - } - - - public function testSelectProperlyCallsPDO() - { - $pdo = $this->getMock(DatabaseConnectionTestMockPDO::class, ['prepare']); - $writePdo = $this->getMock(DatabaseConnectionTestMockPDO::class, ['prepare']); - $writePdo->expects($this->never())->method('prepare'); - $statement = $this->getMock('PDOStatement', ['execute', 'fetchAll']); - $statement->expects($this->once())->method('execute')->with($this->equalTo(['foo' => 'bar'])); - $statement->expects($this->once())->method('fetchAll')->willReturn(['boom']); - $pdo->expects($this->once())->method('prepare')->with('foo')->willReturn($statement); - $mock = $this->getMockConnection(['prepareBindings'], $writePdo); - $mock->setReadPdo($pdo); - $mock->expects($this->once())->method('prepareBindings')->with($this->equalTo(['foo' => 'bar']))->willReturn( - ['foo' => 'bar'] - ); - $results = $mock->select('foo', ['foo' => 'bar']); - $this->assertEquals(['boom'], $results); - $log = $mock->getQueryLog(); - $this->assertEquals('foo', $log[0]['query']); - $this->assertEquals(['foo' => 'bar'], $log[0]['bindings']); - $this->assertIsNumeric($log[0]['time']); - } - - - public function testInsertCallsTheStatementMethod() - { - $connection = $this->getMockConnection(['statement']); - $connection->expects($this->once())->method('statement')->with($this->equalTo('foo'), $this->equalTo(['bar']))->willReturn( - 'baz' - ); - $results = $connection->insert('foo', ['bar']); - $this->assertEquals('baz', $results); - } - - - public function testUpdateCallsTheAffectingStatementMethod() - { - $connection = $this->getMockConnection(['affectingStatement']); - $connection->expects($this->once())->method('affectingStatement')->with($this->equalTo('foo'), $this->equalTo( - ['bar'] - ))->willReturn( - 'baz' - ); - $results = $connection->update('foo', ['bar']); - $this->assertEquals('baz', $results); - } - - - public function testDeleteCallsTheAffectingStatementMethod() - { - $connection = $this->getMockConnection(['affectingStatement']); - $connection->expects($this->once())->method('affectingStatement')->with($this->equalTo('foo'), $this->equalTo( - ['bar'] - ))->willReturn( - 'baz' - ); - $results = $connection->delete('foo', ['bar']); - $this->assertEquals('baz', $results); - } - - - public function testStatementProperlyCallsPDO() - { - $pdo = $this->getMock(DatabaseConnectionTestMockPDO::class, ['prepare']); - $statement = $this->getMock('PDOStatement', ['execute']); - $statement->expects($this->once())->method('execute')->with($this->equalTo(['bar']))->willReturn(true); - $pdo->expects($this->once())->method('prepare')->with($this->equalTo('foo'))->willReturn($statement); - $mock = $this->getMockConnection(['prepareBindings'], $pdo); - $mock->expects($this->once())->method('prepareBindings')->with($this->equalTo(['bar']))->willReturn( - ['bar'] - ); - $results = $mock->statement('foo', ['bar']); - $this->assertEquals(true, $results); - $log = $mock->getQueryLog(); - $this->assertEquals('foo', $log[0]['query']); - $this->assertEquals(['bar'], $log[0]['bindings']); - $this->assertIsNumeric($log[0]['time']); - } - - - public function testAffectingStatementProperlyCallsPDO() - { - $pdo = $this->getMock(DatabaseConnectionTestMockPDO::class, ['prepare']); - $statement = $this->getMock('PDOStatement', ['execute', 'rowCount']); - $statement->expects($this->once())->method('execute')->with($this->equalTo(['foo' => 'bar'])); - $statement->expects($this->once())->method('rowCount')->willReturn(100); - $pdo->expects($this->once())->method('prepare')->with('foo')->willReturn($statement); - $mock = $this->getMockConnection(['prepareBindings'], $pdo); - $mock->expects($this->once())->method('prepareBindings')->with($this->equalTo(['foo' => 'bar']))->willReturn( - ['foo' => 'bar'] - ); - $results = $mock->update('foo', ['foo' => 'bar']); - $this->assertEquals(100, $results); - $log = $mock->getQueryLog(); - $this->assertEquals('foo', $log[0]['query']); - $this->assertEquals(['foo' => 'bar'], $log[0]['bindings']); - $this->assertIsNumeric($log[0]['time']); - } - - - public function testBeganTransactionFiresEventsIfSet() - { - $pdo = $this->createMock(DatabaseConnectionTestMockPDO::class); - $connection = $this->getMockConnection(['getName'], $pdo); - $connection->expects($this->any())->method('getName')->willReturn('name'); - $connection->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('dispatch')->once()->with('connection.name.beganTransaction', $connection); - $connection->beginTransaction(); - } - - - public function testCommitedFiresEventsIfSet() - { - $pdo = $this->getMock(DatabaseConnectionTestMockPDO::class); - $connection = $this->getMockConnection(['getName'], $pdo); - $connection->expects($this->once())->method('getName')->willReturn('name'); - $connection->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('dispatch')->once()->with('connection.name.committed', $connection); - $connection->commit(); - } - - - public function testRollBackedFiresEventsIfSet() - { - $pdo = $this->getMock(DatabaseConnectionTestMockPDO::class); - $connection = $this->getMockConnection(['getName'], $pdo); - $connection->expects($this->once())->method('getName')->willReturn('name'); - $connection->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('dispatch')->once()->with('connection.name.rollingBack', $connection); - $connection->rollBack(); - } - - - public function testTransactionMethodRunsSuccessfully() - { - $pdo = $this->getMock(DatabaseConnectionTestMockPDO::class, ['beginTransaction', 'commit']); - $mock = $this->getMockConnection([], $pdo); - $pdo->expects($this->once())->method('beginTransaction'); - $pdo->expects($this->once())->method('commit'); - $result = $mock->transaction(function($db) { return $db; }); - $this->assertEquals($mock, $result); - } - - - public function testTransactionMethodRollsbackAndThrows() - { - $pdo = $this->getMock(DatabaseConnectionTestMockPDO::class, ['beginTransaction', 'commit', 'rollBack']); - $mock = $this->getMockConnection([], $pdo); - $pdo->expects($this->once())->method('beginTransaction'); - $pdo->expects($this->once())->method('rollBack'); - $pdo->expects($this->never())->method('commit'); - try - { - $mock->transaction(function(): never { throw new Exception('foo'); }); - } - catch (Exception $e) - { - $this->assertEquals('foo', $e->getMessage()); - } - } - - public function testTransactionMethodDisallowPDOChanging() - { - $this->expectException(RuntimeException::class); - $pdo = $this->getMock(DatabaseConnectionTestMockPDO::class, ['beginTransaction', 'commit', 'rollBack']); - $pdo->expects($this->once())->method('beginTransaction'); - $pdo->expects($this->once())->method('rollBack'); - $pdo->expects($this->never())->method('commit'); - - $mock = $this->getMockConnection([], $pdo); - - $mock->setReconnector( - function ($connection) { - $connection->setPDO(null); - } - ); - - $mock->transaction(function ($connection) { $connection->reconnect(); }); - } - - - public function testFromCreatesNewQueryBuilder() - { - $conn = $this->getMockConnection(); - $conn->setQueryGrammar(m::mock(Grammar::class)); - $conn->setPostProcessor(m::mock(Processor::class)); - $builder = $conn->table('users'); - $this->assertInstanceOf(\Illuminate\Database\Query\Builder::class, $builder); - $this->assertEquals('users', $builder->from); - } - - - public function testPrepareBindings() - { - $date = m::mock('DateTime'); - $date->shouldReceive('format')->once()->with('foo')->andReturn('bar'); - $bindings = ['test' => $date]; - $conn = $this->getMockConnection(); - $grammar = m::mock(Grammar::class); - $grammar->shouldReceive('getDateFormat')->once()->andReturn('foo'); - $conn->setQueryGrammar($grammar); - $result = $conn->prepareBindings($bindings); - $this->assertEquals(['test' => 'bar'], $result); - } - - - public function testLogQueryFiresEventsIfSet() - { - $connection = $this->getMockConnection(); - $connection->logQuery('foo', [], time()); - $connection->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('dispatch')->once()->with('illuminate.query', ['foo', [], null, null]); - $connection->logQuery('foo', [], null); - } - - - public function testPretendOnlyLogsQueries() - { - $connection = $this->getMockConnection(); - $queries = $connection->pretend(function($connection) - { - $connection->select('foo bar', ['baz']); - }); - $this->assertEquals('foo bar', $queries[0]['query']); - $this->assertEquals(['baz'], $queries[0]['bindings']); - } - - - public function testSchemaBuilderCanBeCreated() - { - $connection = $this->getMockConnection(); - $schema = $connection->getSchemaBuilder(); - $this->assertInstanceOf(Builder::class, $schema); - $this->assertSame($connection, $schema->getConnection()); - } - - - public function testResolvingPaginatorThroughClosure() - { - $connection = $this->getMockConnection(); - $paginator = m::mock(Factory::class); - $connection->setPaginator(function() use ($paginator) - { - return $paginator; - }); - $this->assertEquals($paginator, $connection->getPaginator()); - } - - - public function testResolvingCacheThroughClosure() - { - $connection = $this->getMockConnection(); - $cache = m::mock(CacheManager::class); - $connection->setCacheManager(function() use ($cache) - { - return $cache; - }); - $this->assertEquals($cache, $connection->getCacheManager()); - } - - - protected function getMockConnection($methods = [], $pdo = null) - { - $pdo = $pdo ?: new DatabaseConnectionTestMockPDO; - $defaults = ['getDefaultQueryGrammar', 'getDefaultPostProcessor', 'getDefaultSchemaGrammar']; - - $connection = $this->getMockBuilder(Connection::class) - ->onlyMethods(array_merge($defaults, $methods)) - ->setConstructorArgs([$pdo]) - ->getMock(); - - $connection->enableQueryLog(); - - return $connection; - } - -} - -class DatabaseConnectionTestMockPDO extends PDO { - public function __construct() { - // - } -} diff --git a/tests/Database/DatabaseConnectorTest.php b/tests/Database/DatabaseConnectorTest.php deleted file mode 100755 index 83f3fb35..00000000 --- a/tests/Database/DatabaseConnectorTest.php +++ /dev/null @@ -1,185 +0,0 @@ -setDefaultOptions([0 => 'foo', 1 => 'bar']); - $this->assertEquals( - [0 => 'baz', 1 => 'bar', 2 => 'boom'], - $connector->getOptions(['options' => [0 => 'baz', 2 => 'boom']]) - ); - } - - - /** - * @dataProvider mySqlConnectProvider - */ - public function testMySqlConnectCallsCreateConnectionWithProperArguments($dsn, $config) - { - $connector = $this->getMock(MySqlConnector::class, ['createConnection', 'getOptions']); - $connection = m::mock('stdClass'); - $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn( - ['options'] - ); - $connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo( - ['options'] - ))->willReturn( - $connection - ); - $connection->shouldReceive('prepare')->once()->with('set names \'utf8\' collate \'utf8_unicode_ci\'')->andReturn($connection); - $connection->shouldReceive('prepare')->once()->with('set session sql_mode=\'\'')->andReturn($connection); - $connection->shouldReceive('execute')->times(2); - $connection->shouldReceive('exec')->zeroOrMoreTimes(); - $result = $connector->connect($config); - - $this->assertSame($result, $connection); - } - - - public function mySqlConnectProvider() - { - return [ - ['mysql:host=foo;dbname=bar', ['host' => 'foo', 'database' => 'bar', 'collation' => 'utf8_unicode_ci', 'charset' => 'utf8']], - ['mysql:host=foo;port=111;dbname=bar', ['host' => 'foo', 'database' => 'bar', 'port' => 111, 'collation' => 'utf8_unicode_ci', 'charset' => 'utf8']], - ['mysql:unix_socket=baz;dbname=bar', ['host' => 'foo', 'database' => 'bar', 'port' => 111, 'unix_socket' => 'baz', 'collation' => 'utf8_unicode_ci', 'charset' => 'utf8']], - ]; - } - - - public function testPostgresConnectCallsCreateConnectionWithProperArguments() - { - $dsn = 'pgsql:host=foo;dbname=bar;port=111'; - $config = ['host' => 'foo', 'database' => 'bar', 'port' => 111, 'charset' => 'utf8']; - $connector = $this->getMock(PostgresConnector::class, ['createConnection', 'getOptions']); - $connection = m::mock('stdClass'); - $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn( - ['options'] - ); - $connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo( - ['options'] - ))->willReturn( - $connection - ); - $connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($connection); - $connection->shouldReceive('execute')->once(); - $result = $connector->connect($config); - - $this->assertSame($result, $connection); - } - - - public function testPostgresSearchPathIsSet() - { - $dsn = 'pgsql:host=foo;dbname=bar'; - $config = ['host' => 'foo', 'database' => 'bar', 'schema' => 'public', 'charset' => 'utf8']; - $connector = $this->getMock(PostgresConnector::class, ['createConnection', 'getOptions']); - $connection = m::mock('stdClass'); - $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn( - ['options'] - ); - $connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo( - ['options'] - ))->willReturn( - $connection - ); - $connection->shouldReceive('prepare')->once()->with('set names \'utf8\'')->andReturn($connection); - $connection->shouldReceive('prepare')->once()->with("set search_path to public")->andReturn($connection); - $connection->shouldReceive('execute')->twice(); - $result = $connector->connect($config); - - $this->assertSame($result, $connection); - } - - - public function testSQLiteMemoryDatabasesMayBeConnectedTo() - { - $dsn = 'sqlite::memory:'; - $config = ['database' => ':memory:']; - $connector = $this->getMock(SQLiteConnector::class, ['createConnection', 'getOptions']); - $connection = m::mock('stdClass'); - $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn( - ['options'] - ); - $connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo( - ['options'] - ))->willReturn( - $connection - ); - $result = $connector->connect($config); - - $this->assertSame($result, $connection); - } - - - public function testSQLiteFileDatabasesMayBeConnectedTo() - { - $dsn = 'sqlite:'.__DIR__; - $config = ['database' => __DIR__]; - $connector = $this->getMock(SQLiteConnector::class, ['createConnection', 'getOptions']); - $connection = m::mock('stdClass'); - $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn( - ['options'] - ); - $connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo( - ['options'] - ))->willReturn( - $connection - ); - $result = $connector->connect($config); - - $this->assertSame($result, $connection); - } - - - public function testSqlServerConnectCallsCreateConnectionWithProperArguments() - { - $config = ['host' => 'foo', 'database' => 'bar', 'port' => 111]; - $dsn = $this->getDsn($config); - $connector = $this->getMock(SqlServerConnector::class, ['createConnection', 'getOptions']); - $connection = m::mock('stdClass'); - $connector->expects($this->once())->method('getOptions')->with($this->equalTo($config))->willReturn( - ['options'] - ); - $connector->expects($this->once())->method('createConnection')->with($this->equalTo($dsn), $this->equalTo($config), $this->equalTo( - ['options'] - ))->willReturn( - $connection - ); - $result = $connector->connect($config); - - $this->assertSame($result, $connection); - } - - protected function getDsn(array $config) - { - extract($config); - - if (in_array('dblib', PDO::getAvailableDrivers())) - { - $port = isset($config['port']) ? ':'.$port : ''; - return "dblib:host={$host}{$port};dbname={$database}"; - } - else - { - $port = isset($config['port']) ? ','.$port : ''; - return "sqlsrv:Server={$host}{$port};Database={$database}"; - } - } - -} diff --git a/tests/Database/DatabaseEloquentBelongsToManyTest.php b/tests/Database/DatabaseEloquentBelongsToManyTest.php deleted file mode 100755 index cd0183a9..00000000 --- a/tests/Database/DatabaseEloquentBelongsToManyTest.php +++ /dev/null @@ -1,506 +0,0 @@ -fill(['name' => 'taylor', 'pivot_user_id' => 1, 'pivot_role_id' => 2]); - $model2 = new EloquentBelongsToManyModelStub; - $model2->fill(['name' => 'dayle', 'pivot_user_id' => 3, 'pivot_role_id' => 4]); - $models = [$model1, $model2]; - - $baseBuilder = m::mock(\Illuminate\Database\Query\Builder::class); - - $relation = $this->getRelation(); - $relation->getParent()->shouldReceive('getConnectionName')->andReturn('foo.connection'); - $relation->getQuery()->shouldReceive('addSelect')->once()->with( - ['roles.*', 'user_role.user_id as pivot_user_id', 'user_role.role_id as pivot_role_id'] - )->andReturn($relation->getQuery()); - $relation->getQuery()->shouldReceive('getModels')->once()->andReturn($models); - $relation->getQuery()->shouldReceive('eagerLoadRelations')->once()->with($models)->andReturn($models); - $relation->getRelated()->shouldReceive('newCollection')->andReturnUsing(function($array) { return new Collection($array); }); - $relation->getQuery()->shouldReceive('getQuery')->once()->andReturn($baseBuilder); - $results = $relation->get(); - - $this->assertInstanceOf(Collection::class, $results); - - // Make sure the foreign keys were set on the pivot models... - $this->assertEquals('user_id', $results[0]->pivot->getForeignKey()); - $this->assertEquals('role_id', $results[0]->pivot->getOtherKey()); - - $this->assertEquals('taylor', $results[0]->name); - $this->assertEquals(1, $results[0]->pivot->user_id); - $this->assertEquals(2, $results[0]->pivot->role_id); - $this->assertEquals('foo.connection', $results[0]->pivot->getConnectionName()); - $this->assertEquals('dayle', $results[1]->name); - $this->assertEquals(3, $results[1]->pivot->user_id); - $this->assertEquals(4, $results[1]->pivot->role_id); - $this->assertEquals('foo.connection', $results[1]->pivot->getConnectionName()); - $this->assertEquals('user_role', $results[0]->pivot->getTable()); - $this->assertTrue($results[0]->pivot->exists); - } - - - public function testTimestampsCanBeRetrievedProperly() - { - $model1 = new EloquentBelongsToManyModelStub; - $model1->fill(['name' => 'taylor', 'pivot_user_id' => 1, 'pivot_role_id' => 2]); - $model2 = new EloquentBelongsToManyModelStub; - $model2->fill(['name' => 'dayle', 'pivot_user_id' => 3, 'pivot_role_id' => 4]); - $models = [$model1, $model2]; - - $baseBuilder = m::mock(\Illuminate\Database\Query\Builder::class); - - $relation = $this->getRelation()->withTimestamps(); - $relation->getParent()->shouldReceive('getConnectionName')->andReturn('foo.connection'); - $relation->getQuery()->shouldReceive('addSelect')->once()->with([ - 'roles.*', - 'user_role.user_id as pivot_user_id', - 'user_role.role_id as pivot_role_id', - 'user_role.created_at as pivot_created_at', - 'user_role.updated_at as pivot_updated_at', - ])->andReturn($relation->getQuery()); - $relation->getQuery()->shouldReceive('getModels')->once()->andReturn($models); - $relation->getQuery()->shouldReceive('eagerLoadRelations')->once()->with($models)->andReturn($models); - $relation->getRelated()->shouldReceive('newCollection')->andReturnUsing(function($array) { return new Collection($array); }); - $relation->getQuery()->shouldReceive('getQuery')->once()->andReturn($baseBuilder); - $results = $relation->get(); - } - - - public function testModelsAreProperlyMatchedToParents() - { - $relation = $this->getRelation(); - - $result1 = new EloquentBelongsToManyModelPivotStub; - $result1->pivot->user_id = 1; - $result2 = new EloquentBelongsToManyModelPivotStub; - $result2->pivot->user_id = 2; - $result3 = new EloquentBelongsToManyModelPivotStub; - $result3->pivot->user_id = 2; - - $model1 = new EloquentBelongsToManyModelStub; - $model1->id = 1; - $model2 = new EloquentBelongsToManyModelStub; - $model2->id = 2; - $model3 = new EloquentBelongsToManyModelStub; - $model3->id = 3; - - $relation->getRelated()->shouldReceive('newCollection')->andReturnUsing(function($array) { return new Collection($array); }); - $models = $relation->match([$model1, $model2, $model3], new Collection([$result1, $result2, $result3]), 'foo'); - - $this->assertEquals(1, $models[0]->foo[0]->pivot->user_id); - $this->assertCount(1, $models[0]->foo); - - $this->assertEquals(2, $models[1]->foo[0]->pivot->user_id); - $this->assertEquals(2, $models[1]->foo[1]->pivot->user_id); - $this->assertCount(2, $models[1]->foo); - $this->assertEmpty($models[2]->foo); - } - - - public function testRelationIsProperlyInitialized() - { - $relation = $this->getRelation(); - $relation->getRelated()->shouldReceive('newCollection')->andReturnUsing(function($array = []) { return new Collection($array); }); - $model = m::mock(Model::class); - $model->shouldReceive('setRelation')->once()->with('foo', m::type(Collection::class)); - $models = $relation->initRelation([$model], 'foo'); - - $this->assertEquals([$model], $models); - } - - - public function testEagerConstraintsAreProperlyAdded() - { - $relation = $this->getRelation(); - $relation->getQuery()->shouldReceive('whereIn')->once()->with('user_role.user_id', [1, 2]); - $model1 = new EloquentBelongsToManyModelStub; - $model1->id = 1; - $model2 = new EloquentBelongsToManyModelStub; - $model2->id = 2; - $relation->addEagerConstraints([$model1, $model2]); - } - - - public function testAttachInsertsPivotTableRecord() - { - $relation = $this->getMock(BelongsToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $query->shouldReceive('insert')->once()->with([['user_id' => 1, 'role_id' => 2, 'foo' => 'bar']])->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->expects($this->once())->method('touchIfTouching'); - - $relation->attach(2, ['foo' => 'bar']); - } - - - public function testAttachMultipleInsertsPivotTableRecord() - { - $relation = $this->getMock(BelongsToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $query->shouldReceive('insert')->once()->with( - [ - ['user_id' => 1, 'role_id' => 2, 'foo' => 'bar'], - ['user_id' => 1, 'role_id' => 3, 'baz' => 'boom', 'foo' => 'bar'], - ] - )->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->expects($this->once())->method('touchIfTouching'); - - $relation->attach([2, 3 => ['baz' => 'boom']], ['foo' => 'bar']); - } - - - public function testAttachInsertsPivotTableRecordWithTimestampsWhenNecessary() - { - $relation = $this->getMock(BelongsToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $relation->withTimestamps(); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $carbon = new Carbon; - $query->shouldReceive('insert')->once()->with( - [['user_id' => 1, 'role_id' => 2, 'foo' => 'bar', 'created_at' => $carbon, 'updated_at' => $carbon]] - )->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->getParent()->shouldReceive('freshTimestamp')->once()->andReturn($carbon); - $relation->expects($this->once())->method('touchIfTouching'); - - $relation->attach(2, ['foo' => 'bar']); - } - - - public function testAttachInsertsPivotTableRecordWithACreatedAtTimestamp() - { - $relation = $this->getMock(BelongsToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $relation->withPivot('created_at'); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $carbon = new Carbon; - $query->shouldReceive('insert')->once()->with( - [['user_id' => 1, 'role_id' => 2, 'foo' => 'bar', 'created_at' => $carbon]] - )->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->getParent()->shouldReceive('freshTimestamp')->once()->andReturn($carbon); - $relation->expects($this->once())->method('touchIfTouching'); - - $relation->attach(2, ['foo' => 'bar']); - } - - - public function testAttachInsertsPivotTableRecordWithAnUpdatedAtTimestamp() - { - $relation = $this->getMock(BelongsToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $relation->withPivot('updated_at'); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $carbon = new Carbon; - $query->shouldReceive('insert')->once()->with( - [['user_id' => 1, 'role_id' => 2, 'foo' => 'bar', 'updated_at' => $carbon]] - )->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->getParent()->shouldReceive('freshTimestamp')->once()->andReturn($carbon); - $relation->expects($this->once())->method('touchIfTouching'); - - $relation->attach(2, ['foo' => 'bar']); - } - - - public function testDetachRemovesPivotTableRecord() - { - $relation = $this->getMock(BelongsToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $query->shouldReceive('where')->once()->with('user_id', 1)->andReturn($query); - $query->shouldReceive('whereIn')->once()->with('role_id', [1, 2, 3]); - $query->shouldReceive('delete')->once()->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->expects($this->once())->method('touchIfTouching'); - - $this->assertTrue($relation->detach([1, 2, 3])); - } - - - public function testDetachWithSingleIDRemovesPivotTableRecord() - { - $relation = $this->getMock(BelongsToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $query->shouldReceive('where')->once()->with('user_id', 1)->andReturn($query); - $query->shouldReceive('whereIn')->once()->with('role_id', [1]); - $query->shouldReceive('delete')->once()->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->expects($this->once())->method('touchIfTouching'); - - $this->assertTrue($relation->detach([1])); - } - - - public function testDetachMethodClearsAllPivotRecordsWhenNoIDsAreGiven() - { - $relation = $this->getMock(BelongsToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $query->shouldReceive('where')->once()->with('user_id', 1)->andReturn($query); - $query->shouldReceive('whereIn')->never(); - $query->shouldReceive('delete')->once()->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->expects($this->once())->method('touchIfTouching'); - - $this->assertTrue($relation->detach()); - } - - - public function testCreateMethodCreatesNewModelAndInsertsAttachmentRecord() - { - $relation = $this->getMock(BelongsToMany::class, ['attach'], $this->getRelationArguments()); - $relation->getRelated()->shouldReceive('newInstance')->once()->andReturn($model = m::mock(Model::class))->with( - ['attributes'] - ); - $model->shouldReceive('save')->once(); - $model->shouldReceive('getKey')->andReturn('foo'); - $relation->expects($this->once())->method('attach')->with('foo', ['joining']); - - $this->assertEquals($model, $relation->create(['attributes'], ['joining'])); - } - - - /** - * @dataProvider syncMethodListProvider - */ - public function testSyncMethodSyncsIntermediateTableWithGivenArray($list) - { - $relation = $this->getMock(BelongsToMany::class, ['attach', 'detach'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $query->shouldReceive('where')->once()->with('user_id', 1)->andReturn($query); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $query->shouldReceive('pluck')->once()->with('role_id')->andReturn([1, 2, 3]); - $relation->expects($this->once())->method('attach')->with($this->equalTo(4), $this->equalTo([]), $this->equalTo(false)); - $relation->expects($this->once())->method('detach')->with($this->equalTo([1])); - $relation->getRelated()->shouldReceive('touches')->andReturn(false); - $relation->getParent()->shouldReceive('touches')->andReturn(false); - - $this->assertEquals(['attached' => [4], 'detached' => [1], 'updated' => []], $relation->sync($list)); - } - - - public function syncMethodListProvider() - { - return [ - [[2, 3, 4]], - [['2', '3', '4']], - ]; - } - - - public function testSyncMethodSyncsIntermediateTableWithGivenArrayAndAttributes() - { - $relation = $this->getMock(BelongsToMany::class, ['attach', 'detach', 'touchIfTouching', 'updateExistingPivot'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $query->shouldReceive('where')->once()->with('user_id', 1)->andReturn($query); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $query->shouldReceive('pluck')->once()->with('role_id')->andReturn([1, 2, 3]); - $relation->expects($this->once())->method('attach')->with($this->equalTo(4), $this->equalTo(['foo' => 'bar']), $this->equalTo(false)); - $relation->expects($this->once())->method('updateExistingPivot')->with($this->equalTo(3), $this->equalTo( - ['baz' => 'qux'] - ), $this->equalTo(false))->willReturn( - true - ); - $relation->expects($this->once())->method('detach')->with($this->equalTo([1])); - $relation->expects($this->once())->method('touchIfTouching'); - - $this->assertEquals( - ['attached' => [4], 'detached' => [1], 'updated' => [3]], $relation->sync( - [2, 3 => ['baz' => 'qux'], 4 => ['foo' => 'bar']] - )); - } - - - public function testSyncMethodDoesntReturnValuesThatWereNotUpdated() - { - $relation = $this->getMock(BelongsToMany::class, ['attach', 'detach', 'touchIfTouching', 'updateExistingPivot'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $query->shouldReceive('where')->once()->with('user_id', 1)->andReturn($query); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $query->shouldReceive('pluck')->once()->with('role_id')->andReturn([1, 2, 3]); - $relation->expects($this->once())->method('attach')->with($this->equalTo(4), $this->equalTo(['foo' => 'bar']), $this->equalTo(false)); - $relation->expects($this->once())->method('updateExistingPivot')->with($this->equalTo(3), $this->equalTo( - ['baz' => 'qux'] - ), $this->equalTo(false))->willReturn( - false - ); - $relation->expects($this->once())->method('detach')->with($this->equalTo([1])); - $relation->expects($this->once())->method('touchIfTouching'); - - $this->assertEquals( - ['attached' => [4], 'detached' => [1], 'updated' => []], $relation->sync( - [2, 3 => ['baz' => 'qux'], 4 => ['foo' => 'bar']] - )); - } - - - public function testTouchMethodSyncsTimestamps() - { - $relation = $this->getRelation(); - $relation->getRelated()->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at'); - $carbon = new Carbon; - $relation->getRelated()->shouldReceive('freshTimestamp')->andReturn($carbon); - $relation->getRelated()->shouldReceive('getQualifiedKeyName')->andReturn('table.id'); - $relation->getQuery()->shouldReceive('select')->once()->with('table.id')->andReturn($relation->getQuery()); - $relation->getQuery()->shouldReceive('pluck')->once()->with('id')->andReturn([1, 2, 3]); - $relation->getRelated()->shouldReceive('newQuery')->once()->andReturn($query = m::mock(Builder::class)); - $query->shouldReceive('whereIn')->once()->with('id', [1, 2, 3])->andReturn($query); - $query->shouldReceive('update')->once()->with(['updated_at' => $carbon]); - - $relation->touch(); - } - - - public function testTouchIfTouching() - { - $relation = $this->getMock(BelongsToMany::class, ['touch', 'touchingParent'], $this->getRelationArguments()); - $relation->expects($this->once())->method('touchingParent')->willReturn(true); - $relation->getParent()->shouldReceive('touch')->once(); - $relation->getParent()->shouldReceive('touches')->once()->with('relation_name')->andReturn(true); - $relation->expects($this->once())->method('touch'); - - $relation->touchIfTouching(); - } - - - public function testSyncMethodConvertsCollectionToArrayOfKeys() - { - $relation = $this->getMock(BelongsToMany::class, ['attach', 'detach', 'touchIfTouching', 'formatSyncList'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - $query->shouldReceive('where')->once()->with('user_id', 1)->andReturn($query); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $query->shouldReceive('pluck')->once()->with('role_id')->andReturn([1, 2, 3]); - - $collection = m::mock(Collection::class); - $collection->shouldReceive('modelKeys')->once()->andReturn([1, 2, 3]); - $relation->expects($this->once())->method('formatSyncList')->with([1, 2, 3])->willReturn( - [1 => [], 2 => [], 3 => []] - ); - $relation->sync($collection); - } - - - public function testWherePivotParamsUsedForNewQueries() - { - $relation = $this->getMock(BelongsToMany::class, ['attach', 'detach', 'touchIfTouching', 'formatSyncList'], $this->getRelationArguments()); - - // we expect to call $relation->wherePivot() - $relation->getQuery()->shouldReceive('where')->once()->andReturn($relation); - - // Our sync() call will produce a new query - $mockQueryBuilder = m::mock('stdClass'); - $query = m::mock('stdClass'); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - - // BelongsToMany::newPivotStatement() sets this - $query->shouldReceive('from')->once()->with('user_role')->andReturn($query); - - // BelongsToMany::newPivotQuery() sets this - $query->shouldReceive('where')->once()->with('user_id', 1)->andReturn($query); - - // This is our test! The wherePivot() params also need to be called - $query->shouldReceive('where')->once()->with('foo', '=', 'bar')->andReturn($query); - - // This is so $relation->sync() works - $query->shouldReceive('pluck')->once()->with('role_id')->andReturn([1, 2, 3]); - $relation->expects($this->once())->method('formatSyncList')->with([1, 2, 3])->willReturn( - [1 => [], 2 => [], 3 => []] - ); - - - $relation = $relation->wherePivot('foo', '=', 'bar'); // these params are to be stored - $relation->sync([1,2,3]); // triggers the whole process above - } - - - public function getRelation() - { - [$builder, $parent] = $this->getRelationArguments(); - - return new BelongsToMany($builder, $parent, 'user_role', 'user_id', 'role_id', 'relation_name'); - } - - - public function getRelationArguments() - { - $parent = m::mock(Model::class); - $parent->shouldReceive('getKey')->andReturn(1); - $parent->shouldReceive('getCreatedAtColumn')->andReturn('created_at'); - $parent->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at'); - - $builder = m::mock(Builder::class); - $related = m::mock(Model::class); - $builder->shouldReceive('getModel')->andReturn($related); - - $related->shouldReceive('getTable')->andReturn('roles'); - $related->shouldReceive('getKeyName')->andReturn('id'); - $related->shouldReceive('newPivot')->andReturnUsing(function() - { - $reflector = new ReflectionClass(Pivot::class); - return $reflector->newInstanceArgs(func_get_args()); - }); - - $builder->shouldReceive('join')->once()->with('user_role', 'roles.id', '=', 'user_role.role_id'); - $builder->shouldReceive('where')->once()->with('user_role.user_id', '=', 1); - - return [$builder, $parent, 'user_role', 'user_id', 'role_id', 'relation_name']; - } - -} - -class EloquentBelongsToManyModelStub extends Illuminate\Database\Eloquent\Model { - protected array $guarded = []; -} - -class EloquentBelongsToManyModelPivotStub extends Illuminate\Database\Eloquent\Model { - public $pivot; - public function __construct() - { - $this->pivot = new EloquentBelongsToManyPivotStub; - } -} - -class EloquentBelongsToManyPivotStub { - public $user_id; -} diff --git a/tests/Database/DatabaseEloquentBelongsToTest.php b/tests/Database/DatabaseEloquentBelongsToTest.php deleted file mode 100755 index 43eef7b4..00000000 --- a/tests/Database/DatabaseEloquentBelongsToTest.php +++ /dev/null @@ -1,108 +0,0 @@ -getRelation(); - $mock = m::mock(Model::class); - $mock->shouldReceive('fill')->once()->with(['attributes'])->andReturn($mock); - $mock->shouldReceive('save')->once()->andReturn(true); - $relation->getQuery()->shouldReceive('first')->once()->andReturn($mock); - - $this->assertTrue($relation->update(['attributes'])); - } - - - public function testEagerConstraintsAreProperlyAdded() - { - $relation = $this->getRelation(); - $relation->getQuery()->shouldReceive('whereIn')->once()->with('relation.id', ['foreign.value', 'foreign.value.two'] - ); - $models = [new EloquentBelongsToModelStub, new EloquentBelongsToModelStub, new AnotherEloquentBelongsToModelStub]; - $relation->addEagerConstraints($models); - } - - - public function testRelationIsProperlyInitialized() - { - $relation = $this->getRelation(); - $model = m::mock(Model::class); - $model->shouldReceive('setRelation')->once()->with('foo', null); - $models = $relation->initRelation([$model], 'foo'); - - $this->assertEquals([$model], $models); - } - - - public function testModelsAreProperlyMatchedToParents() - { - $relation = $this->getRelation(); - $result1 = m::mock('stdClass'); - $result1->shouldReceive('getAttribute')->with('id')->andReturn(1); - $result2 = m::mock('stdClass'); - $result2->shouldReceive('getAttribute')->with('id')->andReturn(2); - $model1 = new EloquentBelongsToModelStub; - $model1->foreign_key = 1; - $model2 = new EloquentBelongsToModelStub; - $model2->foreign_key = 2; - $models = $relation->match([$model1, $model2], new Collection([$result1, $result2]), 'foo'); - - $this->assertEquals(1, $models[0]->foo->getAttribute('id')); - $this->assertEquals(2, $models[1]->foo->getAttribute('id')); - } - - - public function testAssociateMethodSetsForeignKeyOnModel() - { - $parent = m::mock(Model::class); - $parent->shouldReceive('getAttribute')->once()->with('foreign_key')->andReturn('foreign.value'); - $relation = $this->getRelation($parent); - $associate = m::mock(Model::class); - $associate->shouldReceive('getAttribute')->once()->with('id')->andReturn(1); - $parent->shouldReceive('setAttribute')->once()->with('foreign_key', 1); - $parent->shouldReceive('setRelation')->once()->with('relation', $associate); - - $relation->associate($associate); - } - - - protected function getRelation($parent = null) - { - $builder = m::mock(Builder::class); - $builder->shouldReceive('where')->with('relation.id', '=', 'foreign.value'); - $related = m::mock(Model::class); - $related->shouldReceive('getKeyName')->andReturn('id'); - $related->shouldReceive('getTable')->andReturn('relation'); - $builder->shouldReceive('getModel')->andReturn($related); - $parent = $parent ?: new EloquentBelongsToModelStub; - return new BelongsTo($builder, $parent, 'foreign_key', 'id', 'relation'); - } - -} - -class EloquentBelongsToModelStub extends Illuminate\Database\Eloquent\Model { - - public $foreign_key = 'foreign.value'; - -} - -class AnotherEloquentBelongsToModelStub extends Illuminate\Database\Eloquent\Model { - - public $foreign_key = 'foreign.value.two'; - -} diff --git a/tests/Database/DatabaseEloquentBuilderTest.php b/tests/Database/DatabaseEloquentBuilderTest.php deleted file mode 100755 index 1dfec3f2..00000000 --- a/tests/Database/DatabaseEloquentBuilderTest.php +++ /dev/null @@ -1,629 +0,0 @@ -getMockQueryBuilder()]); - $builder->setModel($this->getMockModel()); - $builder->getQuery()->shouldReceive('where')->once()->with('foo_table.foo', '=', 'bar'); - $builder->shouldReceive('first')->with(['column'])->andReturn('baz'); - - $result = $builder->find('bar', ['column']); - $this->assertEquals('baz', $result); - } - - - public function testFindOrNewMethodModelFound() - { - $model = $this->getMockModel(); - $model->shouldReceive('findOrNew')->once()->andReturn('baz'); - - $builder = m::mock('Illuminate\Database\Eloquent\Builder[first]', [$this->getMockQueryBuilder()]); - $builder->setModel($model); - $builder->getQuery()->shouldReceive('where')->once()->with('foo_table.foo', '=', 'bar'); - $builder->shouldReceive('first')->with(['column'])->andReturn('baz'); - - $expected = $model->findOrNew('bar', ['column']); - $result = $builder->find('bar', ['column']); - $this->assertEquals($expected, $result); - } - - - public function testFindOrNewMethodModelNotFound() - { - $model = $this->getMockModel(); - $model->shouldReceive('findOrNew')->once()->andReturn(m::mock(Model::class)); - - $builder = m::mock('Illuminate\Database\Eloquent\Builder[first]', [$this->getMockQueryBuilder()]); - $builder->setModel($model); - $builder->getQuery()->shouldReceive('where')->once()->with('foo_table.foo', '=', 'bar'); - $builder->shouldReceive('first')->with(['column'])->andReturn(null); - - $result = $model->findOrNew('bar', ['column']); - $findResult = $builder->find('bar', ['column']); - $this->assertNull($findResult); - $this->assertInstanceOf(Model::class, $result); - } - - public function testFindOrFailMethodThrowsModelNotFoundException() - { - $this->expectException(Illuminate\Database\Eloquent\ModelNotFoundException::class); - $builder = m::mock('Illuminate\Database\Eloquent\Builder[first]', [$this->getMockQueryBuilder()]); - $builder->setModel($this->getMockModel()); - $builder->getQuery()->shouldReceive('where')->once()->with('foo_table.foo', '=', 'bar'); - $builder->shouldReceive('first')->with(['column'])->andReturn(null); - $result = $builder->findOrFail('bar', ['column']); - } - - public function testFirstOrFailMethodThrowsModelNotFoundException() - { - $this->expectException(Illuminate\Database\Eloquent\ModelNotFoundException::class); - $builder = m::mock('Illuminate\Database\Eloquent\Builder[first]', [$this->getMockQueryBuilder()]); - $builder->setModel($this->getMockModel()); - $builder->shouldReceive('first')->with(['column'])->andReturn(null); - $result = $builder->firstOrFail(['column']); - } - - - public function testFindWithMany() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[get]', [$this->getMockQueryBuilder()]); - $builder->getQuery()->shouldReceive('whereIn')->once()->with('foo_table.foo', [1, 2]); - $builder->setModel($this->getMockModel()); - $builder->shouldReceive('get')->with(['column'])->andReturn('baz'); - - $result = $builder->find([1, 2], ['column']); - $this->assertEquals('baz', $result); - } - - - public function testFirstMethod() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[get,take]', [$this->getMockQueryBuilder()]); - $builder->shouldReceive('take')->with(1)->andReturn($builder); - $builder->shouldReceive('get')->with(['*'])->andReturn(new Collection(['bar'])); - - $result = $builder->first(); - $this->assertEquals('bar', $result); - } - - - public function testGetMethodLoadsModelsAndHydratesEagerRelations() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[getModels,eagerLoadRelations]', [$this->getMockQueryBuilder()] - ); - $builder->shouldReceive('getModels')->with(['foo'])->andReturn(['bar']); - $builder->shouldReceive('eagerLoadRelations')->with(['bar'])->andReturn(['bar', 'baz']); - $builder->setModel($this->getMockModel()); - $builder->getModel()->shouldReceive('newCollection')->with(['bar', 'baz'])->andReturn(new Collection( - ['bar', 'baz'] - )); - - $results = $builder->get(['foo']); - $this->assertEquals(['bar', 'baz'], $results->all()); - } - - - public function testGetMethodDoesntHydrateEagerRelationsWhenNoResultsAreReturned() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[getModels,eagerLoadRelations]', [$this->getMockQueryBuilder()] - ); - $builder->shouldReceive('getModels')->with(['foo'])->andReturn([]); - $builder->shouldReceive('eagerLoadRelations')->never(); - $builder->setModel($this->getMockModel()); - $builder->getModel()->shouldReceive('newCollection')->with([])->andReturn(new Collection([])); - - $results = $builder->get(['foo']); - $this->assertEquals([], $results->all()); - } - - -public function testValueMethodWithModelFound() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[first]', [$this->getMockQueryBuilder()]); - $mockModel = new StdClass; - $mockModel->name = 'foo'; - $builder->shouldReceive('first')->with(['name'])->andReturn($mockModel); - - $this->assertEquals('foo', $builder->value('name')); - } - - - public function testValueMethodWithModelNotFound() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[first]', [$this->getMockQueryBuilder()]); - $builder->shouldReceive('first')->with(['name'])->andReturn(null); - - $this->assertNull($builder->value('name')); - } - - - public function testChunkExecuteCallbackOverPaginatedRequest() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[forPage,get]', [$this->getMockQueryBuilder()]); - $builder->shouldReceive('forPage')->once()->with(1, 2)->andReturn($builder); - $builder->shouldReceive('forPage')->once()->with(2, 2)->andReturn($builder); - $builder->shouldReceive('forPage')->once()->with(3, 2)->andReturn($builder); - $builder->shouldReceive('get')->times(3)->andReturn(['foo1', 'foo2'], ['foo3'], []); - - $callbackExecutionAssertor = m::mock('StdClass'); - $callbackExecutionAssertor->shouldReceive('doSomething')->with('foo1')->once(); - $callbackExecutionAssertor->shouldReceive('doSomething')->with('foo2')->once(); - $callbackExecutionAssertor->shouldReceive('doSomething')->with('foo3')->once(); - - $builder->chunk(2, function($results) use($callbackExecutionAssertor) { - foreach ($results as $result) { - $callbackExecutionAssertor->doSomething($result); - } - }); - } - - - public function testListsReturnsTheMutatedAttributesOfAModel() - { - $builder = $this->getBuilder(); - $builder->getQuery()->shouldReceive('pluck')->with('name', '')->andReturn(['bar', 'baz']); - $builder->setModel($this->getMockModel()); - $builder->getModel()->shouldReceive('hasGetMutator')->with('name')->andReturn(true); - $builder->getModel()->shouldReceive('newFromBuilder')->with(['name' => 'bar'])->andReturn(new EloquentBuilderTestListsStub( - ['name' => 'bar'] - )); - $builder->getModel()->shouldReceive('newFromBuilder')->with(['name' => 'baz'])->andReturn(new EloquentBuilderTestListsStub( - ['name' => 'baz'] - )); - - $this->assertEquals(['foo_bar', 'foo_baz'], $builder->pluck('name')); - } - - - public function testListsWithoutModelGetterJustReturnTheAttributesFoundInDatabase() - { - $builder = $this->getBuilder(); - $builder->getQuery()->shouldReceive('pluck')->with('name', '')->andReturn(['bar', 'baz']); - $builder->setModel($this->getMockModel()); - $builder->getModel()->shouldReceive('hasGetMutator')->with('name')->andReturn(false); - - $this->assertEquals(['bar', 'baz'], $builder->pluck('name')); - } - - - public function testMacrosAreCalledOnBuilder() - { - unset($_SERVER['__test.builder']); - $builder = new Illuminate\Database\Eloquent\Builder(new Illuminate\Database\Query\Builder( - m::mock(ConnectionInterface::class), - m::mock(Grammar::class), - m::mock(Processor::class) - )); - $builder->macro('fooBar', function($builder) - { - $_SERVER['__test.builder'] = $builder; - - return $builder; - }); - $result = $builder->fooBar(); - - $this->assertEquals($builder, $result); - $this->assertEquals($builder, $_SERVER['__test.builder']); - unset($_SERVER['__test.builder']); - } - - - public function testPaginateMethod() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[get]', [$this->getMockQueryBuilder()]); - $builder->setModel($this->getMockModel()); - $builder->getModel()->shouldReceive('getPerPage')->once()->andReturn(15); - $builder->getQuery()->shouldReceive('getPaginationCount')->once()->andReturn(10); - $conn = m::mock('stdClass'); - $paginator = m::mock('stdClass'); - $paginator->shouldReceive('getCurrentPage')->once()->andReturn(1); - $conn->shouldReceive('getPaginator')->once()->andReturn($paginator); - $builder->getQuery()->shouldReceive('getConnection')->once()->andReturn($conn); - $builder->getQuery()->shouldReceive('forPage')->once()->with(1, 15); - $builder->shouldReceive('get')->with(['*'])->andReturn(new Collection(['results'])); - $paginator->shouldReceive('make')->once()->with(['results'], 10, 15)->andReturn(['results']); - - $this->assertEquals(['results'], $builder->paginate()); - } - - - public function testPaginateMethodWithGroupedQuery() - { - $query = $this->getMock(\Illuminate\Database\Query\Builder::class, ['from', 'getConnection'], [ - m::mock(ConnectionInterface::class), - m::mock(Grammar::class), - m::mock(Processor::class), - ]); - $query->expects($this->once())->method('from')->willReturn('foo_table'); - $builder = $this->getMock(Builder::class, ['get'], [$query]); - $builder->setModel($this->getMockModel()); - $builder->getModel()->shouldReceive('getPerPage')->once()->andReturn(2); - $conn = m::mock('stdClass'); - $paginator = m::mock('stdClass'); - $paginator->shouldReceive('getCurrentPage')->once()->andReturn(2); - $conn->shouldReceive('getPaginator')->once()->andReturn($paginator); - $query->expects($this->once())->method('getConnection')->willReturn($conn); - $builder->expects($this->once())->method('get')->with($this->equalTo(['*']))->willReturn( - new Collection(['foo', 'bar', 'baz']) - ); - $paginator->shouldReceive('make')->once()->with(['baz'], 3, 2)->andReturn(['results']); - - $this->assertEquals(['results'], $builder->groupBy('foo')->paginate()); - } - - - public function testQuickPaginateMethod() - { - $query = $this->getMock(\Illuminate\Database\Query\Builder::class, ['from', 'getConnection', 'skip', 'take'], [ - m::mock(ConnectionInterface::class), - m::mock(Grammar::class), - m::mock(Processor::class), - ]); - $query->expects($this->once())->method('from')->willReturn('foo_table'); - $builder = $this->getMock(Builder::class, ['get'], [$query]); - $builder->setModel($this->getMockModel()); - $builder->getModel()->shouldReceive('getPerPage')->once()->andReturn(15); - $conn = m::mock('stdClass'); - $paginator = m::mock('stdClass'); - $paginator->shouldReceive('getCurrentPage')->once()->andReturn(1); - $conn->shouldReceive('getPaginator')->once()->andReturn($paginator); - $query->expects($this->once())->method('getConnection')->willReturn($conn); - $query->expects($this->once())->method('skip')->with(0)->willReturn($query); - $query->expects($this->once())->method('take')->with(16)->willReturn($query); - $builder->expects($this->once())->method('get')->with($this->equalTo(['*']))->willReturn( - new Collection(['results']) - ); - $paginator->shouldReceive('make')->once()->with(['results'], 15)->andReturn(['results']); - - $this->assertEquals(['results'], $builder->simplePaginate()); - } - - - public function testGetModelsProperlyHydratesModels() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[get]', [$this->getMockQueryBuilder()]); - $records[] = ['name' => 'taylor', 'age' => 26]; - $records[] = ['name' => 'dayle', 'age' => 28]; - $builder->getQuery()->shouldReceive('get')->once()->with(['foo'])->andReturn($records); - $model = m::mock('Illuminate\Database\Eloquent\Model[getTable,getConnectionName,newInstance]'); - $model->shouldReceive('getTable')->once()->andReturn('foo_table'); - $builder->setModel($model); - $model->shouldReceive('getConnectionName')->once()->andReturn('foo_connection'); - $model->shouldReceive('newInstance')->andReturnUsing(function() { return new EloquentBuilderTestModelStub; }); - $models = $builder->getModels(['foo']); - - $this->assertEquals('taylor', $models[0]->name); - $this->assertEquals($models[0]->getAttributes(), $models[0]->getOriginal()); - $this->assertEquals('dayle', $models[1]->name); - $this->assertEquals($models[1]->getAttributes(), $models[1]->getOriginal()); - $this->assertEquals('foo_connection', $models[0]->getConnectionName()); - $this->assertEquals('foo_connection', $models[1]->getConnectionName()); - } - - - public function testEagerLoadRelationsLoadTopLevelRelationships() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[loadRelation]', [$this->getMockQueryBuilder()]); - $nop1 = function() {}; - $nop2 = function() {}; - $builder->setEagerLoads(['foo' => $nop1, 'foo.bar' => $nop2]); - $builder->shouldAllowMockingProtectedMethods()->shouldReceive('loadRelation')->with(['models'], 'foo', $nop1)->andReturn( - ['foo'] - ); - - $results = $builder->eagerLoadRelations(['models']); - $this->assertEquals(['foo'], $results); - } - - - public function testRelationshipEagerLoadProcess() - { - $builder = m::mock('Illuminate\Database\Eloquent\Builder[getRelation]', [$this->getMockQueryBuilder()]); - $builder->setEagerLoads(['orders' => function($query) { $_SERVER['__eloquent.constrain'] = $query; }]); - $relation = m::mock('stdClass'); - $relation->shouldReceive('addEagerConstraints')->once()->with(['models']); - $relation->shouldReceive('initRelation')->once()->with(['models'], 'orders')->andReturn(['models']); - $relation->shouldReceive('getEager')->once()->andReturn(['results']); - $relation->shouldReceive('match')->once()->with(['models'], ['results'], 'orders')->andReturn(['models.matched'] - ); - $builder->shouldReceive('getRelation')->once()->with('orders')->andReturn($relation); - $results = $builder->eagerLoadRelations(['models']); - - $this->assertEquals(['models.matched'], $results); - $this->assertEquals($relation, $_SERVER['__eloquent.constrain']); - unset($_SERVER['__eloquent.constrain']); - } - - - public function testGetRelationProperlySetsNestedRelationships() - { - $builder = $this->getBuilder(); - $builder->setModel($this->getMockModel()); - $builder->getModel()->shouldReceive('orders')->once()->andReturn($relation = m::mock('stdClass')); - $relationQuery = m::mock('stdClass'); - $relation->shouldReceive('getQuery')->andReturn($relationQuery); - $relationQuery->shouldReceive('with')->once()->with(['lines' => null, 'lines.details' => null]); - $builder->setEagerLoads(['orders' => null, 'orders.lines' => null, 'orders.lines.details' => null]); - - $relation = $builder->getRelation('orders'); - } - - - public function testGetRelationProperlySetsNestedRelationshipsWithSimilarNames() - { - $builder = $this->getBuilder(); - $builder->setModel($this->getMockModel()); - $builder->getModel()->shouldReceive('orders')->once()->andReturn($relation = m::mock('stdClass')); - $builder->getModel()->shouldReceive('ordersGroups')->once()->andReturn($groupsRelation = m::mock('stdClass')); - - $relationQuery = m::mock('stdClass'); - $relation->shouldReceive('getQuery')->andReturn($relationQuery); - - $groupRelationQuery = m::mock('stdClass'); - $groupsRelation->shouldReceive('getQuery')->andReturn($groupRelationQuery); - $groupRelationQuery->shouldReceive('with')->once()->with(['lines' => null, 'lines.details' => null]); - - $builder->setEagerLoads( - ['orders' => null, 'ordersGroups' => null, 'ordersGroups.lines' => null, 'ordersGroups.lines.details' => null] - ); - - $builder->getRelation('orders'); - $builder->getRelation('ordersGroups'); - } - - - public function testEagerLoadParsingSetsProperRelationships() - { - $builder = $this->getBuilder(); - $builder->with(['orders', 'orders.lines']); - $eagers = $builder->getEagerLoads(); - - $this->assertEquals(['orders', 'orders.lines'], array_keys($eagers)); - $this->assertInstanceOf('Closure', $eagers['orders']); - $this->assertInstanceOf('Closure', $eagers['orders.lines']); - - $builder = $this->getBuilder(); - $builder->with('orders', 'orders.lines'); - $eagers = $builder->getEagerLoads(); - - $this->assertEquals(['orders', 'orders.lines'], array_keys($eagers)); - $this->assertInstanceOf('Closure', $eagers['orders']); - $this->assertInstanceOf('Closure', $eagers['orders.lines']); - - $builder = $this->getBuilder(); - $builder->with(['orders.lines']); - $eagers = $builder->getEagerLoads(); - - $this->assertEquals(['orders', 'orders.lines'], array_keys($eagers)); - $this->assertInstanceOf('Closure', $eagers['orders']); - $this->assertInstanceOf('Closure', $eagers['orders.lines']); - - $builder = $this->getBuilder(); - $builder->with(['orders' => function() { return 'foo'; }]); - $eagers = $builder->getEagerLoads(); - - $this->assertEquals('foo', $eagers['orders']()); - - $builder = $this->getBuilder(); - $builder->with(['orders.lines' => function() { return 'foo'; }]); - $eagers = $builder->getEagerLoads(); - - $this->assertInstanceOf('Closure', $eagers['orders']); - $this->assertNull($eagers['orders']()); - $this->assertEquals('foo', $eagers['orders.lines']()); - } - - - public function testQueryPassThru() - { - $builder = $this->getBuilder(); - $builder->getQuery()->shouldReceive('foobar')->once()->andReturn('foo'); - - $this->assertInstanceOf(Builder::class, $builder->foobar()); - - $builder = $this->getBuilder(); - $builder->getQuery()->shouldReceive('insert')->once()->with(['bar'])->andReturn('foo'); - - $this->assertEquals('foo', $builder->insert(['bar'])); - } - - - public function testQueryScopes() - { - $builder = $this->getBuilder(); - $builder->getQuery()->shouldReceive('from'); - $builder->getQuery()->shouldReceive('where')->once()->with('foo', 'bar'); - $builder->setModel($model = new EloquentBuilderTestScopeStub); - $result = $builder->approved(); - - $this->assertEquals($builder, $result); - } - - - public function testNestedWhere() - { - $nestedQuery = m::mock(Builder::class); - $nestedRawQuery = $this->getMockQueryBuilder(); - $nestedQuery->shouldReceive('getQuery')->once()->andReturn($nestedRawQuery); - $model = $this->getMockModel()->makePartial(); - $model->shouldReceive('newQueryWithoutScopes')->once()->andReturn($nestedQuery); - $builder = $this->getBuilder(); - $builder->getQuery()->shouldReceive('from'); - $builder->setModel($model); - $builder->getQuery()->shouldReceive('addNestedWhereQuery')->once()->with($nestedRawQuery, 'and'); - $nestedQuery->shouldReceive('foo')->once(); - - $result = $builder->where(function($query) { $query->foo(); }); - $this->assertEquals($builder, $result); - } - - - public function testRealNestedWhereWithScopes() - { - $model = new EloquentBuilderTestNestedStub; - $this->mockConnectionForModel($model, 'SQLite'); - $query = $model->newQuery()->where('foo', '=', 'bar')->where(function($query) { $query->where('baz', '>', 9000); }); - $this->assertEquals('select * from "table" where "table"."deleted_at" is null and "foo" = ? and ("baz" > ?)', $query->toSql()); - $this->assertEquals(['bar', 9000], $query->getBindings()); - } - - - public function testSimpleWhere() - { - $builder = $this->getBuilder(); - $builder->getQuery()->shouldReceive('where')->once()->with('foo', '=', 'bar'); - $result = $builder->where('foo', '=', 'bar'); - $this->assertEquals($result, $builder); - } - - - public function testDeleteOverride() - { - $builder = $this->getBuilder(); - $builder->onDelete(function($builder) - { - return ['foo' => $builder]; - }); - $this->assertEquals(['foo' => $builder], $builder->delete()); - } - - - public function testHasNestedWithConstraints() - { - $model = new EloquentBuilderTestModelParentStub; - - $builder = $model->whereHas('foo', function ($q) { - $q->whereHas('bar', function ($q) { - $q->where('baz', 'bam'); - }); - })->toSql(); - - $result = $model->whereHas('foo.bar', function ($q) { - $q->where('baz', 'bam'); - })->toSql(); - - $this->assertEquals($builder, $result); - } - - - public function testHasNested() - { - $model = new EloquentBuilderTestModelParentStub; - - $builder = $model->whereHas('foo', function ($q) { - $q->has('bar'); - }); - - $result = $model->has('foo.bar')->toSql(); - - $this->assertEquals($builder->toSql(), $result); - } - - - protected function mockConnectionForModel($model, $database) - { - $grammarClass = 'Illuminate\Database\Query\Grammars\\'.$database.'Grammar'; - $processorClass = 'Illuminate\Database\Query\Processors\\'.$database.'Processor'; - $grammar = new $grammarClass; - $processor = new $processorClass; - $connection = m::mock(Connection::class, ['getQueryGrammar' => $grammar, 'getPostProcessor' => $processor] - ); - $resolver = m::mock(ConnectionResolverInterface::class, ['connection' => $connection]); - $class = get_class($model); - $class::setConnectionResolver($resolver); - } - - - protected function getBuilder() - { - return new Builder($this->getMockQueryBuilder()); - } - - - protected function getMockModel() - { - $model = m::mock(Model::class); - $model->shouldReceive('getKeyName')->andReturn('foo'); - $model->shouldReceive('getTable')->andReturn('foo_table'); - $model->shouldReceive('getQualifiedKeyName')->andReturn('foo_table.foo'); - return $model; - } - - - protected function getMockQueryBuilder() - { - $query = m::mock(\Illuminate\Database\Query\Builder::class); - $query->shouldReceive('from')->with('foo_table'); - return $query; - } - -} - -class EloquentBuilderTestModelStub extends Illuminate\Database\Eloquent\Model {} - -class EloquentBuilderTestScopeStub extends Illuminate\Database\Eloquent\Model { - public function scopeApproved($query) - { - $query->where('foo', 'bar'); - } -} - -class EloquentBuilderTestWithTrashedStub extends Illuminate\Database\Eloquent\Model { - use Illuminate\Database\Eloquent\SoftDeletes; - protected string $table = 'table'; - #[\Override] - public function getKeyName(): string { return 'foo'; } -} - -class EloquentBuilderTestNestedStub extends Illuminate\Database\Eloquent\Model { - protected string $table = 'table'; - use Illuminate\Database\Eloquent\SoftDeletes; -} - -class EloquentBuilderTestListsStub { - protected $attributes; - public function __construct($attributes) - { - $this->attributes = $attributes; - } - public function __get($key) - { - return 'foo_' . $this->attributes[$key]; - } -} - -class EloquentBuilderTestModelParentStub extends Illuminate\Database\Eloquent\Model { - public function foo() - { - return $this->belongsTo('EloquentBuilderTestModelCloseRelatedStub'); - } -} - -class EloquentBuilderTestModelCloseRelatedStub extends Illuminate\Database\Eloquent\Model { - public function bar() - { - return $this->hasMany('EloquentBuilderTestModelFarRelatedStub'); - } -} - -class EloquentBuilderTestModelFarRelatedStub extends Illuminate\Database\Eloquent\Model {} diff --git a/tests/Database/DatabaseEloquentCollectionTest.php b/tests/Database/DatabaseEloquentCollectionTest.php deleted file mode 100755 index e8b2d8cd..00000000 --- a/tests/Database/DatabaseEloquentCollectionTest.php +++ /dev/null @@ -1,225 +0,0 @@ -add('bar')->add('baz'); - $this->assertEquals(['foo', 'bar', 'baz'], $c->all()); - } - - - public function testGettingMaxItemsFromCollection() - { - $c = new Collection([(object) ['foo' => 10], (object) ['foo' => 20]]); - $this->assertEquals(20, $c->max('foo')); - } - - - public function testGettingMinItemsFromCollection() - { - $c = new Collection([(object) ['foo' => 10], (object) ['foo' => 20]]); - $this->assertEquals(10, $c->min('foo')); - } - - - public function testContainsIndicatesIfModelInArray() - { - $mockModel = m::mock(Model::class); - $mockModel->shouldReceive('getKey')->andReturn(1); - $mockModel2 = m::mock(Model::class); - $mockModel2->shouldReceive('getKey')->andReturn(2); - $mockModel3 = m::mock(Model::class); - $mockModel3->shouldReceive('getKey')->andReturn(3); - $c = new Collection([$mockModel, $mockModel2]); - - $this->assertTrue($c->contains($mockModel)); - $this->assertTrue($c->contains($mockModel2)); - $this->assertFalse($c->contains($mockModel3)); - } - - - public function testContainsIndicatesIfKeyedModelInArray() - { - $mockModel = m::mock(Model::class); - $mockModel->shouldReceive('getKey')->andReturn(1); - $c = new Collection([$mockModel]); - $mockModel2 = m::mock(Model::class); - $mockModel2->shouldReceive('getKey')->andReturn(2); - $c->add($mockModel2); - - $this->assertTrue($c->contains(1)); - $this->assertTrue($c->contains(2)); - $this->assertFalse($c->contains(3)); - } - - - public function testFindMethodFindsModelById() - { - $mockModel = m::mock(Model::class); - $mockModel->shouldReceive('getKey')->andReturn(1); - $c = new Collection([$mockModel]); - - $this->assertSame($mockModel, $c->find(1)); - $this->assertSame('taylor', $c->find(2, 'taylor')); - } - - - public function testLoadMethodEagerLoadsGivenRelationships() - { - $c = $this->getMock(Collection::class, ['first'], [['foo']]); - $mockItem = m::mock('StdClass'); - $c->expects($this->once())->method('first')->willReturn($mockItem); - $mockItem->shouldReceive('newQuery')->once()->andReturn($mockItem); - $mockItem->shouldReceive('with')->with(['bar', 'baz'])->andReturn($mockItem); - $mockItem->shouldReceive('eagerLoadRelations')->once()->with(['foo'])->andReturn(['results']); - $c->load('bar', 'baz'); - - $this->assertEquals(['results'], $c->all()); - } - - - public function testCollectionDictionaryReturnsModelKeys() - { - $one = m::mock(Model::class); - $one->shouldReceive('getKey')->andReturn(1); - - $two = m::mock(Model::class); - $two->shouldReceive('getKey')->andReturn(2); - - $three = m::mock(Model::class); - $three->shouldReceive('getKey')->andReturn(3); - - $c = new Collection([$one, $two, $three]); - - $this->assertEquals([1,2,3], $c->modelKeys()); - } - - - public function testCollectionMergesWithGivenCollection() - { - $one = m::mock(Model::class); - $one->shouldReceive('getKey')->andReturn(1); - - $two = m::mock(Model::class); - $two->shouldReceive('getKey')->andReturn(2); - - $three = m::mock(Model::class); - $three->shouldReceive('getKey')->andReturn(3); - - $c1 = new Collection([$one, $two]); - $c2 = new Collection([$two, $three]); - - $this->assertEquals(new Collection([$one, $two, $three]), $c1->merge($c2)); - } - - - public function testCollectionDiffsWithGivenCollection() - { - $one = m::mock(Model::class); - $one->shouldReceive('getKey')->andReturn(1); - - $two = m::mock(Model::class); - $two->shouldReceive('getKey')->andReturn(2); - - $three = m::mock(Model::class); - $three->shouldReceive('getKey')->andReturn(3); - - $c1 = new Collection([$one, $two]); - $c2 = new Collection([$two, $three]); - - $this->assertEquals(new Collection([$one]), $c1->diff($c2)); - } - - - public function testCollectionIntersectsWithGivenCollection() - { - $one = m::mock(Model::class); - $one->shouldReceive('getKey')->andReturn(1); - - $two = m::mock(Model::class); - $two->shouldReceive('getKey')->andReturn(2); - - $three = m::mock(Model::class); - $three->shouldReceive('getKey')->andReturn(3); - - $c1 = new Collection([$one, $two]); - $c2 = new Collection([$two, $three]); - - $this->assertEquals(new Collection([$two]), $c1->intersect($c2)); - } - - - public function testCollectionReturnsUniqueItems() - { - $one = m::mock(Model::class); - $one->shouldReceive('getKey')->andReturn(1); - - $two = m::mock(Model::class); - $two->shouldReceive('getKey')->andReturn(2); - - $c = new Collection([$one, $two, $two]); - - $this->assertEquals(new Collection([$one, $two]), $c->unique()); - } - - - public function testPluck() - { - $data = new Collection( - [(object) ['name' => 'taylor', 'email' => 'foo'], (object) ['name' => 'dayle', 'email' => 'bar']] - ); - $this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->pluck('email', 'name')->all()); - $this->assertEquals(['foo', 'bar'], $data->pluck('email')->all()); - } - - - public function testOnlyReturnsCollectionWithGivenModelKeys() - { - $one = m::mock(Model::class); - $one->shouldReceive('getKey')->andReturn(1); - - $two = m::mock(Model::class); - $two->shouldReceive('getKey')->andReturn(2); - - $three = m::mock(Model::class); - $three->shouldReceive('getKey')->andReturn(3); - - $c = new Collection([$one, $two, $three]); - - $this->assertEquals(new Collection([$one]), $c->only(1)); - $this->assertEquals(new Collection([$two, $three]), $c->only([2, 3])); - } - - - public function testExceptReturnsCollectionWithoutGivenModelKeys() - { - $one = m::mock(Model::class); - $one->shouldReceive('getKey')->andReturn(1); - - $two = m::mock(Model::class); - $two->shouldReceive('getKey')->andReturn('2'); - - $three = m::mock(Model::class); - $three->shouldReceive('getKey')->andReturn(3); - - $c = new Collection([$one, $two, $three]); - - $this->assertEquals(new Collection([$one, $three]), $c->except(2)); - $this->assertEquals(new Collection([$one]), $c->except([2, 3])); - } - -} diff --git a/tests/Database/DatabaseEloquentHasManyTest.php b/tests/Database/DatabaseEloquentHasManyTest.php deleted file mode 100755 index 9b1d6565..00000000 --- a/tests/Database/DatabaseEloquentHasManyTest.php +++ /dev/null @@ -1,115 +0,0 @@ -getRelation(); - $created = $this->getMock(Model::class, ['save', 'getKey', 'setAttribute']); - $created->expects($this->once())->method('save')->willReturn(true); - $relation->getRelated()->shouldReceive('newInstance')->once()->with(['name' => 'taylor'])->andReturn($created); - $created->expects($this->once())->method('setAttribute')->with('foreign_key', 1); - - $this->assertEquals($created, $relation->create(['name' => 'taylor'])); - } - - - public function testUpdateMethodUpdatesModelsWithTimestamps() - { - $relation = $this->getRelation(); - $relation->getRelated()->shouldReceive('usesTimestamps')->once()->andReturn(true); - $relation->getRelated()->shouldReceive('freshTimestamp')->once()->andReturn($carbon = new Carbon()); - $relation->getRelated()->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at'); - $relation->getQuery()->shouldReceive('update')->once()->with(['foo' => 'bar', 'updated_at' => $carbon])->andReturn('results'); - - $this->assertEquals('results', $relation->update(['foo' => 'bar'])); - } - - - public function testRelationIsProperlyInitialized() - { - $relation = $this->getRelation(); - $model = m::mock(Model::class); - $relation->getRelated()->shouldReceive('newCollection')->andReturnUsing(function($array = []) { return new Collection($array); }); - $model->shouldReceive('setRelation')->once()->with('foo', m::type(Collection::class)); - $models = $relation->initRelation([$model], 'foo'); - - $this->assertEquals([$model], $models); - } - - - public function testEagerConstraintsAreProperlyAdded() - { - $relation = $this->getRelation(); - $relation->getQuery()->shouldReceive('whereIn')->once()->with('table.foreign_key', [1, 2]); - $model1 = new EloquentHasManyModelStub; - $model1->id = 1; - $model2 = new EloquentHasManyModelStub; - $model2->id = 2; - $relation->addEagerConstraints([$model1, $model2]); - } - - - public function testModelsAreProperlyMatchedToParents() - { - $relation = $this->getRelation(); - - $result1 = new EloquentHasManyModelStub; - $result1->foreign_key = 1; - $result2 = new EloquentHasManyModelStub; - $result2->foreign_key = 2; - $result3 = new EloquentHasManyModelStub; - $result3->foreign_key = 2; - - $model1 = new EloquentHasManyModelStub; - $model1->id = 1; - $model2 = new EloquentHasManyModelStub; - $model2->id = 2; - $model3 = new EloquentHasManyModelStub; - $model3->id = 3; - - $relation->getRelated()->shouldReceive('newCollection')->andReturnUsing(function($array) { return new Collection($array); }); - $models = $relation->match([$model1, $model2, $model3], new Collection([$result1, $result2, $result3]), 'foo'); - - $this->assertEquals(1, $models[0]->foo[0]->foreign_key); - $this->assertCount(1, $models[0]->foo); - $this->assertEquals(2, $models[1]->foo[0]->foreign_key); - $this->assertEquals(2, $models[1]->foo[1]->foreign_key); - $this->assertCount(2, $models[1]->foo); - $this->assertEmpty($models[2]->foo); - } - - - protected function getRelation() - { - $builder = m::mock(Builder::class); - $builder->shouldReceive('where')->with('table.foreign_key', '=', 1); - $related = m::mock(Model::class); - $builder->shouldReceive('getModel')->andReturn($related); - $parent = m::mock(Model::class); - $parent->shouldReceive('getAttribute')->with('id')->andReturn(1); - $parent->shouldReceive('getCreatedAtColumn')->andReturn('created_at'); - $parent->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at'); - return new HasMany($builder, $parent, 'table.foreign_key', 'id'); - } - -} - -class EloquentHasManyModelStub extends Illuminate\Database\Eloquent\Model { - public $foreign_key = 'foreign.value'; -} diff --git a/tests/Database/DatabaseEloquentHasManyThroughTest.php b/tests/Database/DatabaseEloquentHasManyThroughTest.php deleted file mode 100644 index 98906fd1..00000000 --- a/tests/Database/DatabaseEloquentHasManyThroughTest.php +++ /dev/null @@ -1,104 +0,0 @@ -getRelation(); - $model = m::mock(Model::class); - $relation->getRelated()->shouldReceive('newCollection')->andReturnUsing( - function ($array = []) { - return new Collection($array); - } - ); - $model->shouldReceive('setRelation')->once()->with('foo', m::type(Collection::class)); - $models = $relation->initRelation([$model], 'foo'); - - $this->assertEquals([$model], $models); - } - - - public function testEagerConstraintsAreProperlyAdded() - { - $relation = $this->getRelation(); - $relation->getQuery()->shouldReceive('whereIn')->once()->with('users.country_id', [1, 2]); - $model1 = new EloquentHasManyThroughModelStub; - $model1->id = 1; - $model2 = new EloquentHasManyThroughModelStub; - $model2->id = 2; - $relation->addEagerConstraints([$model1, $model2]); - } - - - public function testModelsAreProperlyMatchedToParents() - { - $relation = $this->getRelation(); - - $result1 = new EloquentHasManyThroughModelStub; - $result1->country_id = 1; - $result2 = new EloquentHasManyThroughModelStub; - $result2->country_id = 2; - $result3 = new EloquentHasManyThroughModelStub; - $result3->country_id = 2; - - $model1 = new EloquentHasManyThroughModelStub; - $model1->id = 1; - $model2 = new EloquentHasManyThroughModelStub; - $model2->id = 2; - $model3 = new EloquentHasManyThroughModelStub; - $model3->id = 3; - - $relation->getRelated()->shouldReceive('newCollection')->andReturnUsing(function($array) { return new Collection($array); }); - $models = $relation->match([$model1, $model2, $model3], new Collection([$result1, $result2, $result3]), 'foo'); - - $this->assertEquals(1, $models[0]->foo[0]->country_id); - $this->assertCount(1, $models[0]->foo); - $this->assertEquals(2, $models[1]->foo[0]->country_id); - $this->assertEquals(2, $models[1]->foo[1]->country_id); - $this->assertCount(2, $models[1]->foo); - $this->assertEmpty($models[2]->foo); - } - - - protected function getRelation() - { - $builder = m::mock(Builder::class); - $builder->shouldReceive('join')->once()->with('users', 'users.id', '=', 'posts.user_id'); - $builder->shouldReceive('where')->with('users.country_id', '=', 1); - - $country = m::mock(Model::class); - $country->shouldReceive('getKey')->andReturn(1); - $country->shouldReceive('getForeignKey')->andReturn('country_id'); - $user = m::mock(Model::class); - $user->shouldReceive('getTable')->andReturn('users'); - $user->shouldReceive('getQualifiedKeyName')->andReturn('users.id'); - $post = m::mock(Model::class); - $post->shouldReceive('getTable')->andReturn('posts'); - - $builder->shouldReceive('getModel')->andReturn($post); - - $user->shouldReceive('getKey')->andReturn(1); - $user->shouldReceive('getCreatedAtColumn')->andReturn('created_at'); - $user->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at'); - return new HasManyThrough($builder, $country, $user, 'country_id', 'user_id'); - } - -} - -class EloquentHasManyThroughModelStub extends Illuminate\Database\Eloquent\Model { - public $country_id = 'foreign.value'; -} diff --git a/tests/Database/DatabaseEloquentHasOneTest.php b/tests/Database/DatabaseEloquentHasOneTest.php deleted file mode 100755 index be3792d3..00000000 --- a/tests/Database/DatabaseEloquentHasOneTest.php +++ /dev/null @@ -1,139 +0,0 @@ -getRelation(); - $mockModel = $this->getMock(Model::class, ['save']); - $mockModel->expects($this->once())->method('save')->willReturn(true); - $result = $relation->save($mockModel); - - $attributes = $result->getAttributes(); - $this->assertEquals(1, $attributes['foreign_key']); - } - - - public function testCreateMethodProperlyCreatesNewModel() - { - $relation = $this->getRelation(); - $created = $this->getMock(Model::class, ['save', 'getKey', 'setAttribute']); - $created->expects($this->once())->method('save')->willReturn(true); - $relation->getRelated()->shouldReceive('newInstance')->once()->with(['name' => 'taylor'])->andReturn($created); - $created->expects($this->once())->method('setAttribute')->with('foreign_key', 1); - - $this->assertEquals($created, $relation->create(['name' => 'taylor'])); - } - - - public function testUpdateMethodUpdatesModelsWithTimestamps() - { - $relation = $this->getRelation(); - $relation->getRelated()->shouldReceive('usesTimestamps')->once()->andReturn(true); - $relation->getRelated()->shouldReceive('freshTimestamp')->once()->andReturn($carbon = new Carbon()); - $relation->getRelated()->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at'); - $relation->getQuery()->shouldReceive('update')->once()->with(['foo' => 'bar', 'updated_at' => $carbon])->andReturn('results'); - - $this->assertEquals('results', $relation->update(['foo' => 'bar'])); - } - - - public function testRelationIsProperlyInitialized() - { - $relation = $this->getRelation(); - $model = m::mock(Model::class); - $model->shouldReceive('setRelation')->once()->with('foo', null); - $models = $relation->initRelation([$model], 'foo'); - - $this->assertEquals([$model], $models); - } - - - public function testEagerConstraintsAreProperlyAdded() - { - $relation = $this->getRelation(); - $relation->getQuery()->shouldReceive('whereIn')->once()->with('table.foreign_key', [1, 2]); - $model1 = new EloquentHasOneModelStub; - $model1->id = 1; - $model2 = new EloquentHasOneModelStub; - $model2->id = 2; - $relation->addEagerConstraints([$model1, $model2]); - } - - - public function testModelsAreProperlyMatchedToParents() - { - $relation = $this->getRelation(); - - $result1 = new EloquentHasOneModelStub; - $result1->foreign_key = 1; - $result2 = new EloquentHasOneModelStub; - $result2->foreign_key = 2; - - $model1 = new EloquentHasOneModelStub; - $model1->id = 1; - $model2 = new EloquentHasOneModelStub; - $model2->id = 2; - $model3 = new EloquentHasOneModelStub; - $model3->id = 3; - - $models = $relation->match([$model1, $model2, $model3], new Collection([$result1, $result2]), 'foo'); - - $this->assertEquals(1, $models[0]->foo->foreign_key); - $this->assertEquals(2, $models[1]->foo->foreign_key); - $this->assertNull($models[2]->foo); - } - - - public function testRelationCountQueryCanBeBuilt() - { - $relation = $this->getRelation(); - $query = m::mock(Builder::class); - $query->shouldReceive('select')->once()->with(m::type(Expression::class)); - $relation->getParent()->shouldReceive('getTable')->andReturn('table'); - $query->shouldReceive('where')->once()->with('table.foreign_key', '=', m::type( - Expression::class - )); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($parentQuery = m::mock('StdClass')); - $parentQuery->shouldReceive('getGrammar')->once()->andReturn($grammar = m::mock('StdClass')); - $grammar->shouldReceive('wrap')->once()->with('table.id'); - - $relation->getRelationCountQuery($query, $query); - } - - - protected function getRelation() - { - $builder = m::mock(Builder::class); - $builder->shouldReceive('where')->with('table.foreign_key', '=', 1); - $related = m::mock(Model::class); - $builder->shouldReceive('getModel')->andReturn($related); - $parent = m::mock(Model::class); - $parent->shouldReceive('getAttribute')->with('id')->andReturn(1); - $parent->shouldReceive('getCreatedAtColumn')->andReturn('created_at'); - $parent->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at'); - $parent->shouldReceive('newQueryWithoutScopes')->andReturn($builder); - return new HasOne($builder, $parent, 'table.foreign_key', 'id'); - } - -} - -class EloquentHasOneModelStub extends Illuminate\Database\Eloquent\Model { - public $foreign_key = 'foreign.value'; -} diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php deleted file mode 100755 index 11f5e639..00000000 --- a/tests/Database/DatabaseEloquentModelTest.php +++ /dev/null @@ -1,1190 +0,0 @@ -name = 'foo'; - $this->assertEquals('foo', $model->name); - $this->assertTrue(isset($model->name)); - unset($model->name); - $this->assertFalse(isset($model->name)); - - // test mutation - $model->list_items = ['name' => 'taylor']; - $this->assertEquals(['name' => 'taylor'], $model->list_items); - $attributes = $model->getAttributes(); - $this->assertEquals(json_encode(['name' => 'taylor']), $attributes['list_items']); - } - - - public function testDirtyAttributes(): void - { - $model = new EloquentModelStub(['foo' => '1', 'bar' => 2, 'baz' => 3]); - $model->syncOriginal(); - $model->foo = 1; - $model->bar = 20; - $model->baz = 30; - - $this->assertTrue($model->isDirty()); - $this->assertFalse($model->isDirty('foo')); - $this->assertTrue($model->isDirty('bar')); - $this->assertTrue($model->isDirty('foo', 'bar')); - $this->assertTrue($model->isDirty(['foo', 'bar'])); - } - - - public function testCalculatedAttributes(): void - { - $model = new EloquentModelStub; - $model->password = 'secret'; - $attributes = $model->getAttributes(); - - // ensure password attribute was not set to null - $this->assertFalse(array_key_exists('password', $attributes)); - $this->assertEquals('******', $model->password); - $this->assertEquals('5ebe2294ecd0e0f08eab7690d2a6ee69', $attributes['password_hash']); - $this->assertEquals('5ebe2294ecd0e0f08eab7690d2a6ee69', $model->password_hash); - } - - - public function testNewInstanceReturnsNewInstanceWithAttributesSet(): void - { - $model = new EloquentModelStub; - $instance = $model->newInstance(['name' => 'taylor']); - $this->assertInstanceOf('EloquentModelStub', $instance); - $this->assertEquals('taylor', $instance->name); - } - - - public function testHydrateCreatesCollectionOfModels(): void - { - $data = [['name' => 'Taylor'], ['name' => 'Otwell']]; - $collection = EloquentModelStub::hydrate($data); - - $this->assertInstanceOf(Collection::class, $collection); - $this->assertCount(2, $collection); - $this->assertInstanceOf('EloquentModelStub', $collection[0]); - $this->assertInstanceOf('EloquentModelStub', $collection[1]); - $this->assertEquals('Taylor', $collection[0]->name); - $this->assertEquals('Otwell', $collection[1]->name); - } - - - public function testHydrateRawMakesRawQuery(): void - { - $collection = EloquentModelHydrateRawStub::hydrateRaw('SELECT ?', ['foo']); - $this->assertEquals('hydrated', $collection[0]); - } - - - public function testCreateMethodSavesNewModel(): void - { - $_SERVER['__eloquent.saved'] = false; - $model = EloquentModelSaveStub::create(['name' => 'taylor']); - $this->assertTrue($_SERVER['__eloquent.saved']); - $this->assertEquals('taylor', $model->name); - } - - - public function testFindMethodCallsQueryBuilderCorrectly(): void - { - $result = EloquentModelFindStub::find(1); - $this->assertEquals('foo', $result); - } - - - public function testFindMethodUseWritePdo(): void - { - EloquentModelFindWithWritePdoStub::onWriteConnection()->find(1); - } - - - public function testFindOrFailMethodThrowsModelNotFoundException(): void - { - $this->expectException(Illuminate\Database\Eloquent\ModelNotFoundException::class); - $result = EloquentModelFindNotFoundStub::findOrFail(1); - } - - - public function testFindMethodWithArrayCallsQueryBuilderCorrectly(): void - { - $result = EloquentModelFindManyStub::find([1, 2]); - $this->assertEquals('foo', $result); - } - - - public function testDestroyMethodCallsQueryBuilderCorrectly(): void - { - $result = EloquentModelDestroyStub::destroy(1, 2, 3); - } - - - public function testWithMethodCallsQueryBuilderCorrectly(): void - { - $result = EloquentModelWithStub::with('foo', 'bar'); - $this->assertEquals('foo', $result); - } - - - public function testWithMethodCallsQueryBuilderCorrectlyWithArray(): void - { - $result = EloquentModelWithStub::with(['foo', 'bar']); - $this->assertEquals('foo', $result); - } - - - public function testUpdateProcess(): void - { - $model = $this->getMock('EloquentModelStub', ['newQueryWithoutScopes', 'updateTimestamps']); - $query = m::mock(Builder::class); - $query->shouldReceive('where')->once()->with('id', '=', 1); - $query->shouldReceive('update')->once()->with(['name' => 'taylor']); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->expects($this->once())->method('updateTimestamps'); - $model->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('dispatch')->once()->with('eloquent.updated: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('dispatch')->once()->with('eloquent.saved: '.get_class($model), $model)->andReturn(true); - - $model->id = 1; - $model->foo = 'bar'; - // make sure foo isn't synced so we can test that dirty attributes only are updated - $model->syncOriginal(); - $model->name = 'taylor'; - $model->exists = true; - $this->assertTrue($model->save()); - } - - - public function testUpdateProcessDoesntOverrideTimestamps(): void - { - $model = $this->getMock('EloquentModelStub', ['newQueryWithoutScopes']); - $query = m::mock(Builder::class); - $query->shouldReceive('where')->once()->with('id', '=', 1); - $query->shouldReceive('update')->once()->with(['created_at' => 'foo', 'updated_at' => 'bar']); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('until'); - $events->shouldReceive('dispatch'); - - $model->id = 1; - $model->syncOriginal(); - $model->created_at = 'foo'; - $model->updated_at = 'bar'; - $model->exists = true; - $this->assertTrue($model->save()); - } - - - public function testSaveIsCancelledIfSavingEventReturnsFalse(): void - { - $model = $this->getMock('EloquentModelStub', ['newQueryWithoutScopes']); - $query = m::mock(Builder::class); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($model), $model)->andReturn(false); - $model->exists = true; - - $this->assertFalse($model->save()); - } - - - public function testUpdateIsCancelledIfUpdatingEventReturnsFalse(): void - { - $model = $this->getMock('EloquentModelStub', ['newQueryWithoutScopes']); - $query = m::mock(Builder::class); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($model), $model)->andReturn(false); - $model->exists = true; - $model->foo = 'bar'; - - $this->assertFalse($model->save()); - } - - - public function testUpdateProcessWithoutTimestamps(): void - { - $model = $this->getMock('EloquentModelStub', ['newQueryWithoutScopes', 'updateTimestamps', 'fireModelEvent']); - $model->timestamps = false; - $query = m::mock(Builder::class); - $query->shouldReceive('where')->once()->with('id', '=', 1); - $query->shouldReceive('update')->once()->with(['name' => 'taylor']); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->expects($this->never())->method('updateTimestamps'); - $model->expects($this->any())->method('fireModelEvent')->willReturn(true); - - $model->id = 1; - $model->syncOriginal(); - $model->name = 'taylor'; - $model->exists = true; - $this->assertTrue($model->save()); - } - - - public function testUpdateUsesOldPrimaryKey(): void - { - $model = $this->getMock('EloquentModelStub', ['newQueryWithoutScopes', 'updateTimestamps']); - $query = m::mock(Builder::class); - $query->shouldReceive('where')->once()->with('id', '=', 1); - $query->shouldReceive('update')->once()->with(['id' => 2, 'foo' => 'bar']); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->expects($this->once())->method('updateTimestamps'); - $model->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('dispatch')->once()->with('eloquent.updated: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('dispatch')->once()->with('eloquent.saved: '.get_class($model), $model)->andReturn(true); - - $model->id = 1; - $model->syncOriginal(); - $model->id = 2; - $model->foo = 'bar'; - $model->exists = true; - - $this->assertTrue($model->save()); - } - - - public function testTimestampsAreReturnedAsObjects(): void - { - $model = $this->getMock('EloquentDateModelStub', ['getDateFormat']); - $model->expects($this->any())->method('getDateFormat')->willReturn('Y-m-d'); - $model->setRawAttributes([ - 'created_at' => '2012-12-04', - 'updated_at' => '2012-12-05', - ]); - - $this->assertInstanceOf(Carbon::class, $model->created_at); - $this->assertInstanceOf(Carbon::class, $model->updated_at); - } - - - public function testTimestampsAreReturnedAsObjectsFromPlainDatesAndTimestamps(): void - { - $model = $this->getMock('EloquentDateModelStub', ['getDateFormat']); - $model->expects($this->any())->method('getDateFormat')->willReturn('Y-m-d H:i:s'); - $model->setRawAttributes([ - 'created_at' => '2012-12-04', - 'updated_at' => time(), - ]); - - $this->assertInstanceOf(Carbon::class, $model->created_at); - $this->assertInstanceOf(Carbon::class, $model->updated_at); - } - - - public function testTimestampsAreReturnedAsObjectsOnCreate(): void - { - $timestamps = [ - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now() - ]; - $model = new EloquentDateModelStub; - Illuminate\Database\Eloquent\Model::setConnectionResolver($resolver = m::mock( - ConnectionResolverInterface::class - )); - $mockConnection = m::mock(Connection::class); - $mockConnection->allows()->getQueryGrammar()->andReturns($mockConnection); - $mockConnection->allows()->getDateFormat()->andReturn('Y-m-d H:i:s'); - $resolver->allows()->connection()->withAnyArgs()->andReturn($mockConnection); - - $instance = $model->newInstance($timestamps); - $this->assertInstanceOf(Carbon::class, $instance->updated_at); - $this->assertInstanceOf(Carbon::class, $instance->created_at); - } - - - public function testDateTimeAttributesReturnNullIfSetToNull(): void - { - $timestamps = [ - 'created_at' => Carbon::now(), - 'updated_at' => Carbon::now() - ]; - $model = new EloquentDateModelStub; - Illuminate\Database\Eloquent\Model::setConnectionResolver($resolver = m::mock( - ConnectionResolverInterface::class - )); - $resolver->shouldReceive('connection')->andReturn($mockConnection = m::mock(Connection::class)); - $mockConnection->shouldReceive('getQueryGrammar')->andReturn($mockConnection); - $mockConnection->shouldReceive('getDateFormat')->andReturn('Y-m-d H:i:s'); - $instance = $model->newInstance($timestamps); - - $instance->created_at = null; - $this->assertNull($instance->created_at); - } - - - public function testTimestampsAreCreatedFromStringsAndIntegers(): void - { - $model = new EloquentDateModelStub; - $model->created_at = '2013-05-22 00:00:00'; - $this->assertInstanceOf(Carbon::class, $model->created_at); - - $model = new EloquentDateModelStub; - $model->created_at = time(); - $this->assertInstanceOf(Carbon::class, $model->created_at); - - $model = new EloquentDateModelStub; - $model->created_at = '2012-01-01'; - $this->assertInstanceOf(Carbon::class, $model->created_at); - } - - - public function testInsertProcess(): void - { - $model = $this->getMock('EloquentModelStub', ['newQueryWithoutScopes', 'updateTimestamps']); - $query = m::mock(Builder::class); - $query->shouldReceive('insertGetId')->once()->with(['name' => 'taylor'], 'id')->andReturn(1); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->expects($this->once())->method('updateTimestamps'); - - $model->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('dispatch')->once()->with('eloquent.created: '.get_class($model), $model); - $events->shouldReceive('dispatch')->once()->with('eloquent.saved: '.get_class($model), $model); - - $model->name = 'taylor'; - $model->exists = false; - $this->assertTrue($model->save()); - $this->assertEquals(1, $model->id); - $this->assertTrue($model->exists); - - $model = $this->getMock('EloquentModelStub', ['newQueryWithoutScopes', 'updateTimestamps']); - $query = m::mock(Builder::class); - $query->shouldReceive('insert')->once()->with(['name' => 'taylor']); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->expects($this->once())->method('updateTimestamps'); - $model->setIncrementing(false); - - $model->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('dispatch')->once()->with('eloquent.created: '.get_class($model), $model); - $events->shouldReceive('dispatch')->once()->with('eloquent.saved: '.get_class($model), $model); - - $model->name = 'taylor'; - $model->exists = false; - $this->assertTrue($model->save()); - $this->assertNull($model->id); - $this->assertTrue($model->exists); - } - - - public function testInsertIsCancelledIfCreatingEventReturnsFalse(): void - { - $model = $this->getMock('EloquentModelStub', ['newQueryWithoutScopes']); - $query = m::mock(Builder::class); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); - $events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($model), $model)->andReturn(false); - - $this->assertFalse($model->save()); - $this->assertFalse($model->exists); - } - - - public function testDeleteProperlyDeletesModel(): void - { - $model = $this->getMock(Model::class, ['newQueryWithoutScopes', 'updateTimestamps', 'touchOwners']); - $query = m::mock(Builder::class); - $query->shouldReceive('where')->once()->with('id', 1)->andReturn($query); - $query->shouldReceive('delete')->once(); - $model->expects($this->once())->method('newQueryWithoutScopes')->willReturn($query); - $model->expects($this->once())->method('touchOwners'); - $model->exists = true; - $model->id = 1; - $model->delete(); - } - - - public function testNewQueryReturnsEloquentQueryBuilder(): void - { - $conn = m::mock(Connection::class); - $grammar = m::mock(Grammar::class); - $processor = m::mock(Processor::class); - $conn->shouldReceive('getQueryGrammar')->once()->andReturn($grammar); - $conn->shouldReceive('getPostProcessor')->once()->andReturn($processor); - EloquentModelStub::setConnectionResolver($resolver = m::mock( - ConnectionResolverInterface::class - )); - $resolver->shouldReceive('connection')->andReturn($conn); - $model = new EloquentModelStub; - $builder = $model->newQuery(); - $this->assertInstanceOf(Builder::class, $builder); - } - - - public function testGetAndSetTableOperations(): void - { - $model = new EloquentModelStub; - $this->assertEquals('stub', $model->getTable()); - $model->setTable('foo'); - $this->assertEquals('foo', $model->getTable()); - } - - - public function testGetKeyReturnsValueOfPrimaryKey(): void - { - $model = new EloquentModelStub; - $model->id = 1; - $this->assertEquals(1, $model->getKey()); - $this->assertEquals('id', $model->getKeyName()); - } - - - public function testConnectionManagement(): void - { - EloquentModelStub::setConnectionResolver($resolver = m::mock( - ConnectionResolverInterface::class - )); - $model = new EloquentModelStub; - $model->setConnection('foo'); - $resolver->shouldReceive('connection')->once()->with('foo')->andReturn($connection = m::mock(Connection::class)); - - $this->assertEquals($connection, $model->getConnection()); - } - - - public function testToArray(): void - { - $model = new EloquentModelStub; - $model->name = 'foo'; - $model->age = null; - $model->password = 'password1'; - $model->setHidden(['password']); - $model->setRelation('names', new Illuminate\Database\Eloquent\Collection([ - new EloquentModelStub(['bar' => 'baz']), new EloquentModelStub(['bam' => 'boom']) - ])); - $model->setRelation('partner', new EloquentModelStub(['name' => 'abby'])); - $model->setRelation('group', null); - $model->setRelation('multi', new Illuminate\Database\Eloquent\Collection); - $array = $model->toArray(); - - $this->assertIsArray($array); - $this->assertEquals('foo', $array['name']); - $this->assertEquals('baz', $array['names'][0]['bar']); - $this->assertEquals('boom', $array['names'][1]['bam']); - $this->assertEquals('abby', $array['partner']['name']); - $this->assertNull($array['group']); - $this->assertEquals([], $array['multi']); - $this->assertFalse(isset($array['password'])); - - $model->setAppends(['appendable']); - $array = $model->toArray(); - $this->assertEquals('appended', $array['appendable']); - } - - - public function testToArrayIncludesDefaultFormattedTimestamps(): void - { - $model = new EloquentDateModelStub; - $model->setRawAttributes([ - 'created_at' => '2012-12-04', - 'updated_at' => '2012-12-05', - ]); - - $array = $model->toArray(); - - $this->assertEquals('2012-12-04 00:00:00', $array['created_at']); - $this->assertEquals('2012-12-05 00:00:00', $array['updated_at']); - } - - - public function testToArrayIncludesCustomFormattedTimestamps(): void - { - $model = new EloquentDateModelStub; - $model->setRawAttributes([ - 'created_at' => '2012-12-04', - 'updated_at' => '2012-12-05', - ]); - - $array = $model->toArray(); - - $this->assertEquals('2012-12-04 00:00:00', $array['created_at']); - $this->assertEquals('2012-12-05 00:00:00', $array['updated_at']); - } - - - public function testVisibleCreatesArrayWhitelist(): void - { - $model = new EloquentModelStub; - $model->setVisible(['name']); - $model->name = 'Taylor'; - $model->age = 26; - $array = $model->toArray(); - - $this->assertEquals(['name' => 'Taylor'], $array); - } - - - public function testHiddenCanAlsoExcludeRelationships(): void - { - $model = new EloquentModelStub; - $model->name = 'Taylor'; - $model->setRelation('foo', ['bar']); - $model->setHidden(['foo', 'list_items', 'password']); - $array = $model->toArray(); - - $this->assertEquals(['name' => 'Taylor'], $array); - } - - - public function testToArraySnakeAttributes(): void - { - $model = new EloquentModelStub; - $model->setRelation('namesList', new Illuminate\Database\Eloquent\Collection([ - new EloquentModelStub(['bar' => 'baz']), new EloquentModelStub(['bam' => 'boom']) - ])); - $array = $model->toArray(); - - $this->assertEquals('baz', $array['names_list'][0]['bar']); - $this->assertEquals('boom', $array['names_list'][1]['bam']); - - $model = new EloquentModelCamelStub; - $model->setRelation('namesList', new Illuminate\Database\Eloquent\Collection([ - new EloquentModelStub(['bar' => 'baz']), new EloquentModelStub(['bam' => 'boom']) - ])); - $array = $model->toArray(); - - $this->assertEquals('baz', $array['namesList'][0]['bar']); - $this->assertEquals('boom', $array['namesList'][1]['bam']); - } - - - public function testToArrayUsesMutators(): void - { - $model = new EloquentModelStub; - $model->list_items = [1, 2, 3]; - $array = $model->toArray(); - - $this->assertEquals([1, 2, 3], $array['list_items']); - } - - - public function testFillable(): void - { - $model = new EloquentModelStub; - $model->fillable(['name', 'age']); - $model->fill(['name' => 'foo', 'age' => 'bar']); - $this->assertEquals('foo', $model->name); - $this->assertEquals('bar', $model->age); - } - - - public function testUnguardAllowsAnythingToBeSet(): void - { - $model = new EloquentModelStub; - EloquentModelStub::unguard(); - $model->guard(['*']); - $model->fill(['name' => 'foo', 'age' => 'bar']); - $this->assertEquals('foo', $model->name); - $this->assertEquals('bar', $model->age); - EloquentModelStub::setUnguardState(false); - } - - - public function testUnderscorePropertiesAreNotFilled(): void - { - $model = new EloquentModelStub; - $model->fill(['_method' => 'PUT']); - $this->assertEquals([], $model->getAttributes()); - } - - - public function testGuarded(): void - { - $model = new EloquentModelStub; - $model->guard(['name', 'age']); - $model->fill(['name' => 'foo', 'age' => 'bar', 'foo' => 'bar']); - $this->assertFalse(isset($model->name)); - $this->assertFalse(isset($model->age)); - $this->assertEquals('bar', $model->foo); - } - - - public function testFillableOverridesGuarded(): void - { - $model = new EloquentModelStub; - $model->guard(['name', 'age']); - $model->fillable(['age', 'foo']); - $model->fill(['name' => 'foo', 'age' => 'bar', 'foo' => 'bar']); - $this->assertFalse(isset($model->name)); - $this->assertEquals('bar', $model->age); - $this->assertEquals('bar', $model->foo); - } - - - public function testGlobalGuarded(): void - { - $this->expectException(Illuminate\Database\Eloquent\MassAssignmentException::class); - $model = new EloquentModelStub; - $model->guard(['*']); - $model->fill(['name' => 'foo', 'age' => 'bar', 'votes' => 'baz']); - } - - - public function testHasOneCreatesProperRelation(): void - { - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->hasOne('EloquentModelSaveStub'); - $this->assertEquals('save_stub.eloquent_model_stub_id', $relation->getForeignKey()); - - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->hasOne('EloquentModelSaveStub', 'foo'); - $this->assertEquals('save_stub.foo', $relation->getForeignKey()); - $this->assertSame($model, $relation->getParent()); - $this->assertInstanceOf('EloquentModelSaveStub', $relation->getQuery()->getModel()); - } - - - public function testMorphOneCreatesProperRelation(): void - { - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->morphOne('EloquentModelSaveStub', 'morph'); - $this->assertEquals('save_stub.morph_id', $relation->getForeignKey()); - $this->assertEquals('save_stub.morph_type', $relation->getMorphType()); - $this->assertEquals('EloquentModelStub', $relation->getMorphClass()); - } - - - public function testHasManyCreatesProperRelation(): void - { - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->hasMany('EloquentModelSaveStub'); - $this->assertEquals('save_stub.eloquent_model_stub_id', $relation->getForeignKey()); - - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->hasMany('EloquentModelSaveStub', 'foo'); - $this->assertEquals('save_stub.foo', $relation->getForeignKey()); - $this->assertSame($model, $relation->getParent()); - $this->assertInstanceOf('EloquentModelSaveStub', $relation->getQuery()->getModel()); - } - - - public function testMorphManyCreatesProperRelation(): void - { - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->morphMany('EloquentModelSaveStub', 'morph'); - $this->assertEquals('save_stub.morph_id', $relation->getForeignKey()); - $this->assertEquals('save_stub.morph_type', $relation->getMorphType()); - $this->assertEquals('EloquentModelStub', $relation->getMorphClass()); - } - - - public function testBelongsToCreatesProperRelation(): void - { - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->belongsToStub(); - $this->assertEquals('belongs_to_stub_id', $relation->getForeignKey()); - $this->assertSame($model, $relation->getParent()); - $this->assertInstanceOf('EloquentModelSaveStub', $relation->getQuery()->getModel()); - - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->belongsToExplicitKeyStub(); - $this->assertEquals('foo', $relation->getForeignKey()); - } - - - public function testMorphToCreatesProperRelation(): void - { - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->morphToStub(); - $this->assertEquals('morph_to_stub_id', $relation->getForeignKey()); - $this->assertSame($model, $relation->getParent()); - $this->assertInstanceOf('EloquentModelSaveStub', $relation->getQuery()->getModel()); - } - - - public function testBelongsToManyCreatesProperRelation(): void - { - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->belongsToMany('EloquentModelSaveStub'); - $this->assertEquals('eloquent_model_save_stub_eloquent_model_stub.eloquent_model_stub_id', $relation->getForeignKey()); - $this->assertEquals('eloquent_model_save_stub_eloquent_model_stub.eloquent_model_save_stub_id', $relation->getOtherKey()); - $this->assertSame($model, $relation->getParent()); - $this->assertInstanceOf('EloquentModelSaveStub', $relation->getQuery()->getModel()); - $this->assertEquals(__FUNCTION__, $relation->getRelationName()); - - $model = new EloquentModelStub; - $this->addMockConnection($model); - $relation = $model->belongsToMany('EloquentModelSaveStub', 'table', 'foreign', 'other'); - $this->assertEquals('table.foreign', $relation->getForeignKey()); - $this->assertEquals('table.other', $relation->getOtherKey()); - $this->assertSame($model, $relation->getParent()); - $this->assertInstanceOf('EloquentModelSaveStub', $relation->getQuery()->getModel()); - } - - - public function testModelsAssumeTheirName(): void - { - $model = new EloquentModelWithoutTableStub; - $this->assertEquals('eloquent_model_without_table_stubs', $model->getTable()); - - require_once __DIR__.'/stubs/EloquentModelNamespacedStub.php'; - $namespacedModel = new Foo\Bar\EloquentModelNamespacedStub; - $this->assertEquals('eloquent_model_namespaced_stubs', $namespacedModel->getTable()); - } - - - public function testTheMutatorCacheIsPopulated(): void - { - $class = new EloquentModelStub; - - $expectedAttributes = [ - 'list_items', - 'password', - 'appendable' - ]; - - $this->assertEquals($expectedAttributes, $class->getMutatedAttributes()); - } - - - public function testCloneModelMakesAFreshCopyOfTheModel(): void - { - $class = new EloquentModelStub; - $class->id = 1; - $class->exists = true; - $class->first = 'taylor'; - $class->last = 'otwell'; - $class->created_at = $class->freshTimestamp(); - $class->updated_at = $class->freshTimestamp(); - $class->setRelation('foo', ['bar']); - - $clone = $class->replicate(); - - $this->assertNull($clone->id); - $this->assertFalse($clone->exists); - $this->assertEquals('taylor', $clone->first); - $this->assertEquals('otwell', $clone->last); - $this->assertObjectNotHasProperty('created_at', $clone); - $this->assertObjectNotHasProperty('updated_at', $clone); - $this->assertEquals(['bar'], $clone->foo); - } - - - public function testModelObserversCanBeAttachedToModels(): void - { - EloquentModelStub::setEventDispatcher($events = m::mock(Dispatcher::class)); - $events->shouldReceive('listen')->once()->with('eloquent.creating: EloquentModelStub', 'EloquentTestObserverStub@creating'); - $events->shouldReceive('listen')->once()->with('eloquent.saved: EloquentModelStub', 'EloquentTestObserverStub@saved'); - $events->shouldReceive('forget'); - EloquentModelStub::observe(new EloquentTestObserverStub); - EloquentModelStub::flushEventListeners(); - } - - - public function testSetObservableEvents(): void - { - $class = new EloquentModelStub; - $class->setObservableEvents(['foo']); - - $this->assertContains('foo', $class->getObservableEvents()); - } - - - public function testAddObservableEvent(): void - { - $class = new EloquentModelStub; - $class->addObservableEvents('foo'); - - $this->assertContains('foo', $class->getObservableEvents()); - } - - public function testAddMultipleObserveableEvents(): void - { - $class = new EloquentModelStub; - $class->addObservableEvents('foo', 'bar'); - - $this->assertContains('foo', $class->getObservableEvents()); - $this->assertContains('bar', $class->getObservableEvents()); - } - - - public function testRemoveObservableEvent(): void - { - $class = new EloquentModelStub; - $class->setObservableEvents(['foo', 'bar']); - $class->removeObservableEvents('bar'); - - $this->assertNotContains('bar', $class->getObservableEvents()); - } - - public function testRemoveMultipleObservableEvents(): void - { - $class = new EloquentModelStub; - $class->setObservableEvents(['foo', 'bar']); - $class->removeObservableEvents('foo', 'bar'); - - $this->assertNotContains('foo', $class->getObservableEvents()); - $this->assertNotContains('bar', $class->getObservableEvents()); - } - - - public function testGetModelAttributeMethodThrowsExceptionIfNotRelation(): void - { - $this->expectException(LogicException::class); - $model = new EloquentModelStub; - $relation = $model->incorrect_relation_stub; - } - - - public function testModelIsBootedOnUnserialize(): void - { - $model = new EloquentModelBootingTestStub; - $this->assertTrue(EloquentModelBootingTestStub::isBooted()); - $model->foo = 'bar'; - $string = serialize($model); - $model = null; - EloquentModelBootingTestStub::unboot(); - $this->assertFalse(EloquentModelBootingTestStub::isBooted()); - $model = unserialize($string); - $this->assertTrue(EloquentModelBootingTestStub::isBooted()); - } - - - public function testAppendingOfAttributes(): void - { - $model = new EloquentModelAppendsStub; - - $this->assertTrue(isset($model->is_admin)); - $this->assertTrue(isset($model->camelCased)); - $this->assertTrue(isset($model->StudlyCased)); - - $this->assertEquals('admin', $model->is_admin); - $this->assertEquals('camelCased', $model->camelCased); - $this->assertEquals('StudlyCased', $model->StudlyCased); - - $model->setHidden(['is_admin', 'camelCased', 'StudlyCased']); - $this->assertEquals([], $model->toArray()); - - $model->setVisible([]); - $this->assertEquals([], $model->toArray()); - } - - - public function testReplicateCreatesANewModelInstanceWithSameAttributeValues(): void - { - $model = new EloquentModelStub; - $model->id = 'id'; - $model->foo = 'bar'; - $model->created_at = new DateTime; - $model->updated_at = new DateTime; - $replicated = $model->replicate(); - - $this->assertNull($replicated->id); - $this->assertEquals('bar', $replicated->foo); - $this->assertNull($replicated->created_at); - $this->assertNull($replicated->updated_at); - } - - - public function testIncrementOnExistingModelCallsQueryAndSetsAttribute(): void - { - $model = m::mock('EloquentModelStub[newQuery]'); - $model->exists = true; - $model->id = 1; - $model->syncOriginalAttribute('id'); - $model->foo = 2; - - $model->allows()->newQuery()->andReturn($query = m::mock(Builder::class)); - $query->allows()->where()->withAnyArgs()->andReturn($query); - $query->allows()->increment()->withAnyArgs()->andReturn(1); - - $model->publicIncrement('foo'); - - $this->assertEquals(3, $model->foo); - $this->assertFalse($model->isDirty()); - } - - public function testRelationshipTouchOwnersIsPropagated(): void - { - $relation = $this->getMockBuilder(BelongsTo::class)->onlyMethods(['touch'])->disableOriginalConstructor()->getMock(); - $relation->expects($this->once())->method('touch'); - - $model = m::mock('EloquentModelStub[partner]'); - $this->addMockConnection($model); - $model->shouldReceive('partner')->once()->andReturn($relation); - $model->setTouchedRelations(['partner']); - - $mockPartnerModel = m::mock('EloquentModelStub[touchOwners]'); - $mockPartnerModel->shouldReceive('touchOwners')->once(); - $model->setRelation('partner', $mockPartnerModel); - - $model->touchOwners(); - } - - - public function testRelationshipTouchOwnersIsNotPropagatedIfNoRelationshipResult(): void - { - $relation = $this->getMockBuilder(BelongsTo::class)->onlyMethods(['touch'])->disableOriginalConstructor()->getMock(); - $relation->expects($this->once())->method('touch'); - - $model = m::mock('EloquentModelStub[partner]'); - $this->addMockConnection($model); - $model->shouldReceive('partner')->once()->andReturn($relation); - $model->setTouchedRelations(['partner']); - - $model->setRelation('partner', null); - - $model->touchOwners(); - } - - - public function testTimestampsAreNotUpdatedWithTimestampsFalseSaveOption(): void - { - $model = m::mock('EloquentModelStub[newQueryWithoutScopes]'); - $query = m::mock(Builder::class); - $query->shouldReceive('where')->once()->with('id', '=', 1); - $query->shouldReceive('update')->once()->with(['name' => 'taylor']); - $model->shouldReceive('newQueryWithoutScopes')->once()->andReturn($query); - - $model->id = 1; - $model->syncOriginal(); - $model->name = 'taylor'; - $model->exists = true; - $this->assertTrue($model->save(['timestamps' => false])); - $this->assertNull($model->updated_at); - } - - - protected function addMockConnection($model): void - { - $model->setConnectionResolver($resolver = m::mock(ConnectionResolverInterface::class)); - $resolver->shouldReceive('connection')->andReturn(m::mock(Connection::class)); - $model->getConnection()->shouldReceive('getQueryGrammar')->andReturn(m::mock( - Grammar::class - )); - $model->getConnection()->shouldReceive('getPostProcessor')->andReturn(m::mock( - Processor::class - )); - } - -} - -class EloquentTestObserverStub { - public function creating(): void - {} - public function saved(): void - {} -} - -class EloquentModelStub extends Illuminate\Database\Eloquent\Model { - protected string $table = 'stub'; - protected array $guarded = []; - protected string $morph_to_stub_type = 'EloquentModelSaveStub'; - public function getListItemsAttribute($value) - { - return json_decode((string) $value, true); - } - public function setListItemsAttribute($value): void - { - $this->attributes['list_items'] = json_encode($value); - } - public function getPasswordAttribute(): string - { - return '******'; - } - public function setPasswordAttribute($value): void - { - $this->attributes['password_hash'] = md5((string) $value); - } - public function publicIncrement($column, $amount = 1): int - { - return $this->increment($column, $amount); - } - public function belongsToStub(): BelongsTo - { - return $this->belongsTo('EloquentModelSaveStub'); - } - public function morphToStub(): \Illuminate\Database\Eloquent\Relations\MorphTo - { - return $this->morphTo(); - } - public function belongsToExplicitKeyStub(): BelongsTo - { - return $this->belongsTo('EloquentModelSaveStub', 'foo'); - } - public function incorrectRelationStub(): string - { - return 'foo'; - } - #[\Override] - public function getDates(): array - { - return []; - } - public function getAppendableAttribute(): string - { - return 'appended'; - } -} - -class EloquentModelCamelStub extends EloquentModelStub { - public static bool $snakeAttributes = false; -} - -class EloquentDateModelStub extends EloquentModelStub { - #[\Override] - public function getDates(): array - { - return ['created_at', 'updated_at']; - } -} - -class EloquentModelSaveStub extends Illuminate\Database\Eloquent\Model { - protected string $table = 'save_stub'; - protected array $guarded = []; - #[\Override] - public function save(array $options = []): bool { $_SERVER['__eloquent.saved'] = true; return true; } - #[\Override] - public function setIncrementing($value): void - { - $this->incrementing = $value; - } -} - -class EloquentModelFindStub extends Illuminate\Database\Eloquent\Model { - #[\Override] - public function newQuery() - { - $mock = m::mock(Builder::class); - $mock->shouldReceive('find')->once()->with(1, ['*'])->andReturn('foo'); - return $mock; - } -} - -class EloquentModelFindWithWritePdoStub extends Illuminate\Database\Eloquent\Model { - #[\Override] - public function newQuery() - { - $mock = m::mock(Builder::class); - $mock->expects('useWritePdo')->andReturnSelf(); - $mock->expects('find')->with(1)->andReturns('foo'); - - return $mock; - } -} - -class EloquentModelFindNotFoundStub extends Illuminate\Database\Eloquent\Model { - #[\Override] - public function newQuery() - { - $mock = m::mock(Builder::class); - $mock->shouldReceive('find')->once()->with(1, ['*'])->andReturn(null); - return $mock; - } -} - -class EloquentModelDestroyStub extends Illuminate\Database\Eloquent\Model { - #[\Override] - public function newQuery() - { - $mock = m::mock(Builder::class); - $mock->shouldReceive('whereIn')->once()->with('id', [1, 2, 3])->andReturn($mock); - $mock->shouldReceive('get')->once()->andReturn([$model = m::mock('StdClass')]); - $model->shouldReceive('delete')->once(); - return $mock; - } -} - -class EloquentModelHydrateRawStub extends Illuminate\Database\Eloquent\Model { - #[\Override] - public static function hydrate(array $items, $connection = null): Collection { return new Collection(['hydrated']); } - #[\Override] - public function getConnection(): Connection - { - $mock = m::mock(Connection::class); - $mock->shouldReceive('select')->once()->with('SELECT ?', ['foo'])->andReturn([]); - return $mock; - } -} - -class EloquentModelFindManyStub extends Illuminate\Database\Eloquent\Model { - #[\Override] - public function newQuery() - { - $mock = m::mock(Builder::class); - $mock->shouldReceive('find')->once()->with([1, 2], ['*'])->andReturn('foo'); - return $mock; - } -} - -class EloquentModelWithStub extends Illuminate\Database\Eloquent\Model { - #[\Override] - public function newQuery() - { - $mock = m::mock(Builder::class); - $mock->shouldReceive('with')->once()->with(['foo', 'bar'])->andReturn('foo'); - return $mock; - } -} - -class EloquentModelWithoutTableStub extends Illuminate\Database\Eloquent\Model {} - -class EloquentModelBootingTestStub extends Illuminate\Database\Eloquent\Model { - public static function unboot(): void - { - unset(static::$booted[static::class]); - } - public static function isBooted(): bool - { - return array_key_exists(static::class, static::$booted); - } -} - -class EloquentModelAppendsStub extends Illuminate\Database\Eloquent\Model { - protected array $appends = ['is_admin', 'camelCased', 'StudlyCased']; - public function getIsAdminAttribute(): string - { - return 'admin'; - } - public function getCamelCasedAttribute(): string - { - return 'camelCased'; - } - public function getStudlyCasedAttribute(): string - { - return 'StudlyCased'; - } -} diff --git a/tests/Database/DatabaseEloquentMorphTest.php b/tests/Database/DatabaseEloquentMorphTest.php deleted file mode 100755 index a34492bd..00000000 --- a/tests/Database/DatabaseEloquentMorphTest.php +++ /dev/null @@ -1,120 +0,0 @@ -getOneRelation(); - } - - - public function testMorphOneEagerConstraintsAreProperlyAdded() - { - $relation = $this->getOneRelation(); - $relation->getQuery()->shouldReceive('whereIn')->once()->with('table.morph_id', [1, 2]); - $relation->getQuery()->shouldReceive('where')->once()->with('table.morph_type', get_class($relation->getParent())); - - $model1 = new EloquentMorphResetModelStub; - $model1->id = 1; - $model2 = new EloquentMorphResetModelStub; - $model2->id = 2; - $relation->addEagerConstraints([$model1, $model2]); - } - - - /** - * Note that the tests are the exact same for morph many because the classes share this code... - * Will still test to be safe. - */ - public function testMorphManySetsProperConstraints() - { - $relation = $this->getManyRelation(); - } - - - public function testMorphManyEagerConstraintsAreProperlyAdded() - { - $relation = $this->getManyRelation(); - $relation->getQuery()->shouldReceive('whereIn')->once()->with('table.morph_id', [1, 2]); - $relation->getQuery()->shouldReceive('where')->once()->with('table.morph_type', get_class($relation->getParent())); - - $model1 = new EloquentMorphResetModelStub; - $model1->id = 1; - $model2 = new EloquentMorphResetModelStub; - $model2->id = 2; - $relation->addEagerConstraints([$model1, $model2]); - } - - - public function testCreateFunctionOnMorph() - { - // Doesn't matter which relation type we use since they share the code... - $relation = $this->getOneRelation(); - $created = m::mock(Model::class); - $created->shouldReceive('setAttribute')->once()->with('morph_id', 1); - $created->shouldReceive('setAttribute')->once()->with('morph_type', get_class($relation->getParent())); - $relation->getRelated()->shouldReceive('newInstance')->once()->with(['name' => 'taylor'])->andReturn($created); - $created->shouldReceive('save')->once()->andReturn(true); - - $this->assertEquals($created, $relation->create(['name' => 'taylor'])); - } - - - protected function getOneRelation() - { - $builder = m::mock(Builder::class); - $builder->shouldReceive('where')->once()->with('table.morph_id', '=', 1); - $related = m::mock(Model::class); - $builder->shouldReceive('getModel')->andReturn($related); - $parent = m::mock(Model::class); - $parent->shouldReceive('getAttribute')->with('id')->andReturn(1); - $parent->shouldReceive('getMorphClass')->andReturn(get_class($parent)); - $builder->shouldReceive('where')->once()->with('table.morph_type', get_class($parent)); - return new MorphOne($builder, $parent, 'table.morph_type', 'table.morph_id', 'id'); - } - - - protected function getManyRelation() - { - $builder = m::mock(Builder::class); - $builder->shouldReceive('where')->once()->with('table.morph_id', '=', 1); - $related = m::mock(Model::class); - $builder->shouldReceive('getModel')->andReturn($related); - $parent = m::mock(Model::class); - $parent->shouldReceive('getAttribute')->with('id')->andReturn(1); - $parent->shouldReceive('getMorphClass')->andReturn(get_class($parent)); - $builder->shouldReceive('where')->once()->with('table.morph_type', get_class($parent)); - return new MorphMany($builder, $parent, 'table.morph_type', 'table.morph_id', 'id'); - } - -} - - -class EloquentMorphResetModelStub extends Illuminate\Database\Eloquent\Model {} - - -class EloquentMorphResetBuilderStub extends Illuminate\Database\Eloquent\Builder { - public function __construct() { $this->query = new EloquentRelationQueryStub; } - #[\Override] - public function getModel() { return new EloquentMorphResetModelStub; } - public function isSoftDeleting() { return false; } -} - - -class EloquentMorphQueryStub extends Illuminate\Database\Query\Builder { - public function __construct() {} -} diff --git a/tests/Database/DatabaseEloquentMorphToManyTest.php b/tests/Database/DatabaseEloquentMorphToManyTest.php deleted file mode 100644 index 0f925721..00000000 --- a/tests/Database/DatabaseEloquentMorphToManyTest.php +++ /dev/null @@ -1,120 +0,0 @@ -getRelation(); - $relation->getQuery()->shouldReceive('whereIn')->once()->with('taggables.taggable_id', [1, 2]); - $relation->getQuery()->shouldReceive('where')->once()->with( - 'taggables.taggable_type', - get_class($relation->getParent()) - ); - $model1 = new EloquentMorphToManyModelStub; - $model1->id = 1; - $model2 = new EloquentMorphToManyModelStub; - $model2->id = 2; - $relation->addEagerConstraints([$model1, $model2]); - } - - - public function testAttachInsertsPivotTableRecord(): void - { - $relation = $this->getMock(MorphToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('taggables')->andReturn($query); - $query->shouldReceive('insert')->once()->with( - [['taggable_id' => 1, 'taggable_type' => get_class($relation->getParent()), 'tag_id' => 2, 'foo' => 'bar']] - )->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->expects($this->once())->method('touchIfTouching'); - - $relation->attach(2, ['foo' => 'bar']); - } - - - public function testDetachRemovesPivotTableRecord(): void - { - $relation = $this->getMock(MorphToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('taggables')->andReturn($query); - $query->shouldReceive('where')->once()->with('taggable_id', 1)->andReturn($query); - $query->shouldReceive('where')->once()->with('taggable_type', get_class($relation->getParent()))->andReturn($query); - $query->shouldReceive('whereIn')->once()->with('tag_id', [1, 2, 3]); - $query->shouldReceive('delete')->once()->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->expects($this->once())->method('touchIfTouching'); - - $this->assertTrue($relation->detach([1, 2, 3])); - } - - - public function testDetachMethodClearsAllPivotRecordsWhenNoIDsAreGiven(): void - { - $relation = $this->getMock(MorphToMany::class, ['touchIfTouching'], $this->getRelationArguments()); - $query = m::mock('stdClass'); - $query->shouldReceive('from')->once()->with('taggables')->andReturn($query); - $query->shouldReceive('where')->once()->with('taggable_id', 1)->andReturn($query); - $query->shouldReceive('where')->once()->with('taggable_type', get_class($relation->getParent()))->andReturn($query); - $query->shouldReceive('whereIn')->never(); - $query->shouldReceive('delete')->once()->andReturn(true); - $relation->getQuery()->shouldReceive('getQuery')->andReturn($mockQueryBuilder = m::mock('StdClass')); - $mockQueryBuilder->shouldReceive('newQuery')->once()->andReturn($query); - $relation->expects($this->once())->method('touchIfTouching'); - - $this->assertTrue($relation->detach()); - } - - - public function getRelation(): MorphToMany - { - [$builder, $parent] = $this->getRelationArguments(); - - return new MorphToMany($builder, $parent, 'taggable', 'taggables', 'taggable_id', 'tag_id'); - } - - - public function getRelationArguments():array - { - $parent = m::mock(Model::class); - $parent->shouldReceive('getMorphClass')->andReturn(get_class($parent)); - $parent->shouldReceive('getKey')->andReturn(1); - $parent->shouldReceive('getCreatedAtColumn')->andReturn('created_at'); - $parent->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at'); - $parent->shouldReceive('getMorphClass')->andReturn(get_class($parent)); - - $builder = m::mock(Builder::class); - $related = m::mock(Model::class); - $builder->shouldReceive('getModel')->andReturn($related); - - $related->shouldReceive('getTable')->andReturn('tags'); - $related->shouldReceive('getKeyName')->andReturn('id'); - $related->shouldReceive('getMorphClass')->andReturn(get_class($related)); - - $builder->shouldReceive('join')->once()->with('taggables', 'tags.id', '=', 'taggables.tag_id'); - $builder->shouldReceive('where')->once()->with('taggables.taggable_id', '=', 1); - $builder->shouldReceive('where')->once()->with('taggables.taggable_type', get_class($parent)); - - return [$builder, $parent, 'taggable', 'taggables', 'taggable_id', 'tag_id', 'relation_name', false]; - } - -} - -class EloquentMorphToManyModelStub extends Illuminate\Database\Eloquent\Model { - protected array $guarded = []; -} diff --git a/tests/Database/DatabaseEloquentMorphToTest.php b/tests/Database/DatabaseEloquentMorphToTest.php deleted file mode 100644 index 07538756..00000000 --- a/tests/Database/DatabaseEloquentMorphToTest.php +++ /dev/null @@ -1,155 +0,0 @@ -getRelation(); - $relation->addEagerConstraints( - [ - $one = (object)['morph_type' => 'morph_type_1', 'foreign_key' => 'foreign_key_1'], - $two = (object) ['morph_type' => 'morph_type_1', 'foreign_key' => 'foreign_key_1'], - $three = (object) ['morph_type' => 'morph_type_2', 'foreign_key' => 'foreign_key_2'], - ] - ); - - $dictionary = $relation->getDictionary(); - - $this->assertEquals([ - 'morph_type_1' => [ - 'foreign_key_1' => [ - $one, - $two - ] - ], - 'morph_type_2' => [ - 'foreign_key_2' => [ - $three - ] - ], - ], $dictionary); - } - - - public function testModelsAreProperlyPulledAndMatched() - { - $relation = $this->getRelation(); - - $one = m::mock('StdClass'); - $one->morph_type = 'morph_type_1'; - $one->foreign_key = 'foreign_key_1'; - - $two = m::mock('StdClass'); - $two->morph_type = 'morph_type_1'; - $two->foreign_key = 'foreign_key_1'; - - $three = m::mock('StdClass'); - $three->morph_type = 'morph_type_2'; - $three->foreign_key = 'foreign_key_2'; - - $relation->addEagerConstraints([$one, $two, $three]); - - $relation->shouldReceive('createModelByType')->once()->with('morph_type_1')->andReturn($firstQuery = m::mock( - Builder::class - )); - $relation->shouldReceive('createModelByType')->once()->with('morph_type_2')->andReturn($secondQuery = m::mock( - Builder::class - )); - $firstQuery->shouldReceive('getKeyName')->andReturn('id'); - $secondQuery->shouldReceive('getKeyName')->andReturn('id'); - - $firstQuery->shouldReceive('newQuery')->once()->andReturn($firstQuery); - $secondQuery->shouldReceive('newQuery')->once()->andReturn($secondQuery); - - $firstQuery->shouldReceive('whereIn')->once()->with('id', ['foreign_key_1'])->andReturn($firstQuery); - $firstQuery->shouldReceive('get')->once()->andReturn(Collection::make([$resultOne = m::mock('StdClass')])); - $resultOne->shouldReceive('getKey')->andReturn('foreign_key_1'); - - $secondQuery->shouldReceive('whereIn')->once()->with('id', ['foreign_key_2'])->andReturn($secondQuery); - $secondQuery->shouldReceive('get')->once()->andReturn(Collection::make([$resultTwo = m::mock('StdClass')])); - $resultTwo->shouldReceive('getKey')->andReturn('foreign_key_2'); - - $one->shouldReceive('setRelation')->once()->with('relation', $resultOne); - $two->shouldReceive('setRelation')->once()->with('relation', $resultOne); - $three->shouldReceive('setRelation')->once()->with('relation', $resultTwo); - - $relation->getEager(); - } - - public function testModelsWithSoftDeleteAreProperlyPulled() - { - $builder = m::mock(Builder::class); - - $relation = $this->getRelation(null, $builder); - - $builder->shouldReceive('getMacro')->once()->with('withTrashed')->andReturn(function() { return true; }); - $builder->shouldReceive('withTrashed')->once(); - - $relation->withTrashed(); - } - - public function testAssociateMethodSetsForeignKeyAndTypeOnModel() - { - $parent = m::mock(Model::class); - $parent->shouldReceive('getAttribute')->once()->with('foreign_key')->andReturn('foreign.value'); - - $relation = $this->getRelationAssociate($parent); - - $associate = m::mock(Model::class); - $associate->shouldReceive('getKey')->once()->andReturn(1); - $associate->shouldReceive('getMorphClass')->once()->andReturn('Model'); - - $parent->shouldReceive('setAttribute')->once()->with('foreign_key', 1); - $parent->shouldReceive('setAttribute')->once()->with('morph_type', 'Model'); - $parent->shouldReceive('setRelation')->once()->with('relation', $associate); - - $relation->associate($associate); - } - - - protected function getRelationAssociate($parent) - { - $builder = m::mock(Builder::class); - $builder->shouldReceive('where')->with('relation.id', '=', 'foreign.value'); - $related = m::mock(Model::class); - $related->shouldReceive('getKey')->andReturn(1); - $related->shouldReceive('getTable')->andReturn('relation'); - $builder->shouldReceive('getModel')->andReturn($related); - return new MorphTo($builder, $parent, 'foreign_key', 'id', 'morph_type', 'relation'); - } - - - public function getRelation($parent = null, $builder = null) - { - $builder = $builder ?: m::mock(Builder::class); - $builder->shouldReceive('where')->with('relation.id', '=', 'foreign.value'); - $related = m::mock(Model::class); - $related->shouldReceive('getKeyName')->andReturn('id'); - $related->shouldReceive('getTable')->andReturn('relation'); - $builder->shouldReceive('getModel')->andReturn($related); - $parent = $parent ?: new EloquentMorphToModelStub; - $morphTo = m::mock('Illuminate\Database\Eloquent\Relations\MorphTo[createModelByType]', [$builder, $parent, 'foreign_key', 'id', 'morph_type', 'relation'] - ); - return $morphTo; - } - -} - - -class EloquentMorphToModelStub extends Illuminate\Database\Eloquent\Model { - public $foreign_key = 'foreign.value'; -} diff --git a/tests/Database/DatabaseEloquentPivotTest.php b/tests/Database/DatabaseEloquentPivotTest.php deleted file mode 100755 index b7b7f6df..00000000 --- a/tests/Database/DatabaseEloquentPivotTest.php +++ /dev/null @@ -1,104 +0,0 @@ -shouldReceive('getConnectionName')->once()->andReturn('connection'); - $pivot = new Pivot($parent, ['foo' => 'bar'], 'table', true); - - $this->assertEquals(['foo' => 'bar'], $pivot->getAttributes()); - $this->assertEquals('connection', $pivot->getConnectionName()); - $this->assertEquals('table', $pivot->getTable()); - $this->assertTrue($pivot->exists); - } - - - public function testPropertiesUnchangedAreNotDirty(): void - { - $parent = m::mock('Illuminate\Database\Eloquent\Model[getConnectionName]'); - $parent->shouldReceive('getConnectionName')->once()->andReturn('connection'); - $pivot = new Pivot($parent, ['foo' => 'bar', 'shimy' => 'shake'], 'table', true); - - $this->assertEquals([], $pivot->getDirty()); - } - - - public function testPropertiesChangedAreDirty(): void - { - $parent = m::mock('Illuminate\Database\Eloquent\Model[getConnectionName]'); - $parent->shouldReceive('getConnectionName')->once()->andReturn('connection'); - $pivot = new Pivot($parent, ['foo' => 'bar', 'shimy' => 'shake'], 'table', true); - $pivot->shimy = 'changed'; - - $this->assertEquals(['shimy' => 'changed'], $pivot->getDirty()); - } - - - public function testTimestampPropertyIsSetIfCreatedAtInAttributes(): void - { - $parent = m::mock('Illuminate\Database\Eloquent\Model[getConnectionName,getDates]'); - $parent->shouldReceive('getConnectionName')->andReturn('connection'); - $parent->shouldReceive('getDates')->andReturn([]); - $pivot = new DatabaseEloquentPivotTestDateStub($parent, ['foo' => 'bar', 'created_at' => 'foo'], 'table'); - $this->assertTrue($pivot->timestamps); - - $pivot = new DatabaseEloquentPivotTestDateStub($parent, ['foo' => 'bar'], 'table'); - $this->assertFalse($pivot->timestamps); - } - - - public function testKeysCanBeSetProperly(): void - { - $parent = m::mock('Illuminate\Database\Eloquent\Model[getConnectionName]'); - $parent->shouldReceive('getConnectionName')->once()->andReturn('connection'); - $pivot = new Pivot($parent, ['foo' => 'bar'], 'table'); - $pivot->setPivotKeys('foreign', 'other'); - - $this->assertEquals('foreign', $pivot->getForeignKey()); - $this->assertEquals('other', $pivot->getOtherKey()); - } - - - public function testDeleteMethodDeletesModelByKeys(): void - { - $parent = m::mock('Illuminate\Database\Eloquent\Model[getConnectionName]'); - $parent->guard([]); - $parent->shouldReceive('getConnectionName')->once()->andReturn('connection'); - $pivot = $this->getMock(Pivot::class, ['newQuery'], [$parent, ['foo' => 'bar'], 'table']); - $pivot->setPivotKeys('foreign', 'other'); - $pivot->foreign = 'foreign.value'; - $pivot->other = 'other.value'; - $query = m::mock('stdClass'); - $query->shouldReceive('where')->once()->with('foreign', 'foreign.value')->andReturn($query); - $query->shouldReceive('where')->once()->with('other', 'other.value')->andReturn($query); - $query->shouldReceive('delete')->once()->andReturn(true); - $pivot->expects($this->once())->method('newQuery')->willReturn($query); - - $this->assertTrue($pivot->delete()); - } - -} - - -class DatabaseEloquentPivotTestModelStub extends Illuminate\Database\Eloquent\Model {} - -class DatabaseEloquentPivotTestDateStub extends Illuminate\Database\Eloquent\Relations\Pivot { - #[\Override] - public function getDates(): array - { - return []; - } -} diff --git a/tests/Database/DatabaseEloquentRelationTest.php b/tests/Database/DatabaseEloquentRelationTest.php deleted file mode 100755 index dfbdcea1..00000000 --- a/tests/Database/DatabaseEloquentRelationTest.php +++ /dev/null @@ -1,118 +0,0 @@ -setRelation('test', $relation); - $parent->setRelation('foo','bar'); - $this->assertTrue(!array_key_exists('foo', $parent->toArray())); - } - - - public function testTouchMethodUpdatesRelatedTimestamps() - { - $builder = m::mock(\Illuminate\Database\Eloquent\Builder::class); - $parent = m::mock(Model::class); - $parent->shouldReceive('getAttribute')->with('id')->andReturn(1); - $builder->shouldReceive('getModel')->andReturn($related = m::mock('StdClass')); - $builder->shouldReceive('where'); - $relation = new HasOne($builder, $parent, 'foreign_key', 'id'); - $related->shouldReceive('getTable')->andReturn('table'); - $related->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at'); - $related->shouldReceive('freshTimestampString')->andReturn(Carbon::now()); - $builder->shouldReceive('update')->once()->with(['updated_at' => Carbon::now()]); - - $relation->touch(); - } - - /** - * Testing to ensure loop does not occur during relational queries in global scopes - * - * Executing parent model's global scopes could result in an infinite loop when the - * parent model's global scope utilizes a relation in a query like has or whereHas - */ - public function testDonNotRunParentModelGlobalScopes() - { - /** @var Mockery\MockInterface $parent */ - $eloquentBuilder = m::mock(\Illuminate\Database\Eloquent\Builder::class); - $queryBuilder = m::mock(Builder::class); - $parent = m::mock('EloquentRelationResetModelStub')->makePartial(); - $grammar = m::mock(Grammar::class); - - $eloquentBuilder->shouldReceive('getModel')->andReturn($related = m::mock('StdClass')); - $eloquentBuilder->shouldReceive('getQuery')->andReturn($queryBuilder); - $queryBuilder->shouldReceive('getGrammar')->andReturn($grammar); - $grammar->shouldReceive('wrap'); - $parent->shouldReceive('newQueryWithoutScopes')->andReturn($eloquentBuilder); - - //Test Condition - $parent->shouldReceive('applyGlobalScopes')->andReturn($eloquentBuilder)->never(); - - $relation = new EloquentRelationStub($eloquentBuilder, $parent); - $relation->wrap('test'); - } - -} - -class EloquentRelationResetModelStub extends Illuminate\Database\Eloquent\Model { - //Override method call which would normally go through __call() - public function getQuery() - { - return $this->newQuery()->getQuery(); - } -} - - -class EloquentRelationResetStub extends Illuminate\Database\Eloquent\Builder { - public function __construct() { $this->query = new EloquentRelationQueryStub; } - #[\Override] - public function getModel() { return new EloquentRelationResetModelStub; } -} - - -class EloquentRelationQueryStub extends Illuminate\Database\Query\Builder { - public function __construct() {} -} - -class EloquentRelationStub extends Relation -{ - public function addConstraints() - { - } - - public function addEagerConstraints(array $models) - { - } - - public function initRelation(array $models, $relation) - { - } - - public function match(array $models, Collection $results, $relation) - { - } - - public function getResults() - { - } -} diff --git a/tests/Database/DatabaseMigrationCreatorTest.php b/tests/Database/DatabaseMigrationCreatorTest.php deleted file mode 100755 index be359143..00000000 --- a/tests/Database/DatabaseMigrationCreatorTest.php +++ /dev/null @@ -1,67 +0,0 @@ -getCreator(); - unset($_SERVER['__migration.creator']); - $creator->afterCreate( - function () { - $_SERVER['__migration.creator'] = true; - } - ); - $creator->expects($this->any())->method('getDatePrefix')->willReturn('foo'); - $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->getStubPath().'/blank.stub')->andReturn('{{class}}'); - $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar'); - - $creator->create('create_bar', 'foo'); - - $this->assertTrue($_SERVER['__migration.creator']); - - unset($_SERVER['__migration.creator']); - } - - - public function testTableUpdateMigrationStoresMigrationFile() - { - $creator = $this->getCreator(); - $creator->expects($this->any())->method('getDatePrefix')->willReturn('foo'); - $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->getStubPath().'/update.stub')->andReturn('{{class}} {{table}}'); - $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar baz'); - - $creator->create('create_bar', 'foo', 'baz'); - } - - - public function testTableCreationMigrationStoresMigrationFile() - { - $creator = $this->getCreator(); - $creator->expects($this->any())->method('getDatePrefix')->willReturn('foo'); - $creator->getFilesystem()->shouldReceive('get')->once()->with($creator->getStubPath().'/create.stub')->andReturn('{{class}} {{table}}'); - $creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar baz'); - - $creator->create('create_bar', 'foo', 'baz', true); - } - - - protected function getCreator() - { - $files = m::mock(Filesystem::class); - - return $this->getMock(MigrationCreator::class, ['getDatePrefix'], [$files]); - } - -} diff --git a/tests/Database/DatabaseMigrationInstallCommandTest.php b/tests/Database/DatabaseMigrationInstallCommandTest.php deleted file mode 100755 index fef7eec3..00000000 --- a/tests/Database/DatabaseMigrationInstallCommandTest.php +++ /dev/null @@ -1,33 +0,0 @@ -shouldReceive('setSource')->once()->with('foo'); - $repo->shouldReceive('createRepository')->once(); - - $this->runCommand($command, ['--database' => 'foo']); - } - - - protected function runCommand($command, $options = []) - { - return $command->run(new Symfony\Component\Console\Input\ArrayInput($options), new Symfony\Component\Console\Output\NullOutput); - } - -} diff --git a/tests/Database/DatabaseMigrationMakeCommandTest.php b/tests/Database/DatabaseMigrationMakeCommandTest.php deleted file mode 100755 index 1065386e..00000000 --- a/tests/Database/DatabaseMigrationMakeCommandTest.php +++ /dev/null @@ -1,98 +0,0 @@ - __DIR__]; - $command->setLaravel($app); - $creator->allows()->create() - ->once() - ->with('create_foo', __DIR__.'/database/migrations', null, false) - ->andReturn($app['path']); - - $this->runCommand($command, ['name' => 'create_foo']); - } - - - public function testBasicCreateGivesCreatorProperArgumentsWhenTableIsSet() - { - $command = new DatabaseMigrationMakeCommandTestStub($creator = m::mock( - MigrationCreator::class - ), __DIR__.'/vendor'); - $app = ['path' => __DIR__]; - $command->setLaravel($app); - $creator->allows()->create() - ->once() - ->with('create_foo', __DIR__.'/database/migrations', 'users', true) - ->andReturn($app['path']); - - $this->runCommand($command, ['name' => 'create_foo', '--create' => 'users']); - } - - - public function testPackagePathsMayBeUsed() - { - $command = new DatabaseMigrationMakeCommandTestStub($creator = m::mock( - MigrationCreator::class - ), __DIR__.'/vendor'); - $app = ['path' => __DIR__]; - $command->setLaravel($app); - $creator->allows()->create() - ->once() - ->with('create_foo', __DIR__.'/vendor/bar/src/migrations', null, false) - ->andReturn($app['path']); - - $this->runCommand($command, ['name' => 'create_foo', '--package' => 'bar']); - } - - - public function testPackageFallsBackToVendorDirWhenNotExplicit() - { - $command = new DatabaseMigrationMakeCommandTestStub($creator = m::mock( - MigrationCreator::class - ), __DIR__.'/vendor'); - $creator->allows()->create() - ->once() - ->with('create_foo', __DIR__.'/vendor/foo/bar/src/migrations', null, false) - ->andReturn(__DIR__); - - $this->runCommand($command, ['name' => 'create_foo', '--package' => 'foo/bar']); - } - - - protected function runCommand($command, $input = []) - { - return $command->run( - new Symfony\Component\Console\Input\ArrayInput($input), - new Symfony\Component\Console\Output\NullOutput - ); - } - -} - - - -class DatabaseMigrationMakeCommandTestStub extends MigrateMakeCommand -{ - #[\Override] - public function call($command, array $arguments = []) - { - // - } -} diff --git a/tests/Database/DatabaseMigrationMigrateCommandTest.php b/tests/Database/DatabaseMigrationMigrateCommandTest.php deleted file mode 100755 index e454aeda..00000000 --- a/tests/Database/DatabaseMigrationMigrateCommandTest.php +++ /dev/null @@ -1,123 +0,0 @@ - __DIR__]); - $command->setLaravel($app); - $migrator->shouldReceive('setConnection')->once()->with(null); - $migrator->shouldReceive('run')->once()->with(__DIR__.'/database/migrations', false); - $migrator->shouldReceive('getNotes')->andReturn([]); - $migrator->shouldReceive('repositoryExists')->once()->andReturn(true); - - $this->runCommand($command); - } - - - public function testMigrationRepositoryCreatedWhenNecessary() - { - $params = [$migrator = m::mock(Migrator::class), __DIR__.'/vendor']; - $command = $this->getMock(MigrateCommand::class, ['call'], $params); - $app = new ApplicationDatabaseMigrationStub(['path' => __DIR__]); - $command->setLaravel($app); - $migrator->shouldReceive('setConnection')->once()->with(null); - $migrator->shouldReceive('run')->once()->with(__DIR__.'/database/migrations', false); - $migrator->shouldReceive('getNotes')->andReturn([]); - $migrator->shouldReceive('repositoryExists')->once()->andReturn(false); - $command->expects($this->once())->method('call')->with($this->equalTo('migrate:install'), $this->equalTo( - ['--database' => null] - )); - - $this->runCommand($command); - } - - - public function testPackageIsRespectedWhenMigrating() - { - $command = new MigrateCommand($migrator = m::mock(Migrator::class), __DIR__.'/vendor'); - $command->setLaravel(new ApplicationDatabaseMigrationStub()); - $migrator->shouldReceive('setConnection')->once()->with(null); - $migrator->shouldReceive('run')->once()->with(__DIR__.'/vendor/bar/src/migrations', false); - $migrator->shouldReceive('getNotes')->andReturn([]); - $migrator->shouldReceive('repositoryExists')->once()->andReturn(true); - - $this->runCommand($command, ['--package' => 'bar']); - } - - - public function testVendorPackageIsRespectedWhenMigrating() - { - $command = new MigrateCommand($migrator = m::mock(Migrator::class), __DIR__.'/vendor'); - $command->setLaravel(new ApplicationDatabaseMigrationStub()); - $migrator->shouldReceive('setConnection')->once()->with(null); - $migrator->shouldReceive('run')->once()->with(__DIR__.'/vendor/foo/bar/src/migrations', false); - $migrator->shouldReceive('getNotes')->andReturn([]); - $migrator->shouldReceive('repositoryExists')->once()->andReturn(true); - - $this->runCommand($command, ['--package' => 'foo/bar']); - } - - - public function testTheCommandMayBePretended() - { - $command = new MigrateCommand($migrator = m::mock(Migrator::class), __DIR__.'/vendor'); - $app = new ApplicationDatabaseMigrationStub(['path' => __DIR__]); - $command->setLaravel($app); - $migrator->shouldReceive('setConnection')->once()->with(null); - $migrator->shouldReceive('run')->once()->with(__DIR__.'/database/migrations', true); - $migrator->shouldReceive('getNotes')->andReturn([]); - $migrator->shouldReceive('repositoryExists')->once()->andReturn(true); - - $this->runCommand($command, ['--pretend' => true]); - } - - - public function testTheDatabaseMayBeSet() - { - $command = new MigrateCommand($migrator = m::mock(Migrator::class), __DIR__.'/vendor'); - $app = new ApplicationDatabaseMigrationStub(['path' => __DIR__]); - $command->setLaravel($app); - $migrator->shouldReceive('setConnection')->once()->with('foo'); - $migrator->shouldReceive('run')->once()->with(__DIR__.'/database/migrations', false); - $migrator->shouldReceive('getNotes')->andReturn([]); - $migrator->shouldReceive('repositoryExists')->once()->andReturn(true); - - $this->runCommand($command, ['--database' => 'foo']); - } - - - protected function runCommand($command, $input = []) - { - return $command->run(new Symfony\Component\Console\Input\ArrayInput($input), new Symfony\Component\Console\Output\NullOutput); - } - -} - -class ApplicationDatabaseMigrationStub implements ArrayAccess { - public $content = []; - public $env = 'development'; - public function __construct(array $data = []) { $this->content = $data; } - public function offsetExists($offset): bool - { return isset($this->content[$offset]); } - public function offsetGet($offset): mixed { return $this->content[$offset]; } - public function offsetSet($offset, $value): void { $this->content[$offset] = $value; } - public function offsetUnset($offset): void { unset($this->content[$offset]); } - public function environment() { return $this->env; } -} diff --git a/tests/Database/DatabaseMigrationRepositoryTest.php b/tests/Database/DatabaseMigrationRepositoryTest.php deleted file mode 100755 index 7f289c7e..00000000 --- a/tests/Database/DatabaseMigrationRepositoryTest.php +++ /dev/null @@ -1,119 +0,0 @@ -getRepository(); - $query = m::mock('stdClass'); - $connectionMock = m::mock(Connection::class); - $repo->getConnectionResolver()->shouldReceive('connection')->with(null)->andReturn($connectionMock); - $repo->getConnection()->shouldReceive('table')->once()->with('migrations')->andReturn($query); - $query->shouldReceive('pluck')->once()->with('migration')->andReturn('bar'); - - $this->assertEquals('bar', $repo->getRan()); - } - - - public function testGetLastMigrationsGetsAllMigrationsWithTheLatestBatchNumber() - { - $repo = $this->getMock(DatabaseMigrationRepository::class, ['getLastBatchNumber'], [ - $resolver = m::mock(ConnectionResolverInterface::class), 'migrations' - ]); - $repo->expects($this->once())->method('getLastBatchNumber')->willReturn(1); - $query = m::mock('stdClass'); - $connectionMock = m::mock(Connection::class); - $repo->getConnectionResolver()->shouldReceive('connection')->with(null)->andReturn($connectionMock); - $repo->getConnection()->shouldReceive('table')->once()->with('migrations')->andReturn($query); - $query->shouldReceive('where')->once()->with('batch', 1)->andReturn($query); - $query->shouldReceive('orderBy')->once()->with('migration', 'desc')->andReturn($query); - $query->shouldReceive('get')->once()->andReturn('foo'); - - $this->assertEquals('foo', $repo->getLast()); - } - - - public function testLogMethodInsertsRecordIntoMigrationTable() - { - $repo = $this->getRepository(); - $query = m::mock('stdClass'); - $connectionMock = m::mock(Connection::class); - $repo->getConnectionResolver()->shouldReceive('connection')->with(null)->andReturn($connectionMock); - $repo->getConnection()->shouldReceive('table')->once()->with('migrations')->andReturn($query); - $query->shouldReceive('insert')->once()->with(['migration' => 'bar', 'batch' => 1]); - - $repo->log('bar', 1); - } - - - public function testDeleteMethodRemovesAMigrationFromTheTable() - { - $repo = $this->getRepository(); - $query = m::mock('stdClass'); - $connectionMock = m::mock(Connection::class); - $repo->getConnectionResolver()->shouldReceive('connection')->with(null)->andReturn($connectionMock); - $repo->getConnection()->shouldReceive('table')->once()->with('migrations')->andReturn($query); - $query->shouldReceive('where')->once()->with('migration', 'foo')->andReturn($query); - $query->shouldReceive('delete')->once(); - $migration = (object) ['migration' => 'foo']; - - $repo->delete($migration); - } - - - public function testGetNextBatchNumberReturnsLastBatchNumberPlusOne() - { - $repo = $this->getMock(DatabaseMigrationRepository::class, ['getLastBatchNumber'], [ - m::mock(ConnectionResolverInterface::class), 'migrations' - ]); - $repo->expects($this->once())->method('getLastBatchNumber')->willReturn(1); - - $this->assertEquals(2, $repo->getNextBatchNumber()); - } - - - public function testGetLastBatchNumberReturnsMaxBatch() - { - $repo = $this->getRepository(); - $query = m::mock('stdClass'); - $connectionMock = m::mock(Connection::class); - $repo->getConnectionResolver()->shouldReceive('connection')->with(null)->andReturn($connectionMock); - $repo->getConnection()->shouldReceive('table')->once()->with('migrations')->andReturn($query); - $query->shouldReceive('max')->once()->andReturn(1); - - $this->assertEquals(1, $repo->getLastBatchNumber()); - } - - - public function testCreateRepositoryCreatesProperDatabaseTable() - { - $repo = $this->getRepository(); - $schema = m::mock('stdClass'); - $connectionMock = m::mock(Connection::class); - $repo->getConnectionResolver()->shouldReceive('connection')->with(null)->andReturn($connectionMock); - $repo->getConnection()->shouldReceive('getSchemaBuilder')->once()->andReturn($schema); - $schema->shouldReceive('create')->once()->with('migrations', m::type('Closure')); - - $repo->createRepository(); - } - - - protected function getRepository() - { - return new DatabaseMigrationRepository(m::mock(ConnectionResolverInterface::class), 'migrations'); - } - -} diff --git a/tests/Database/DatabaseMigrationResetCommandTest.php b/tests/Database/DatabaseMigrationResetCommandTest.php deleted file mode 100755 index 6ac4e627..00000000 --- a/tests/Database/DatabaseMigrationResetCommandTest.php +++ /dev/null @@ -1,50 +0,0 @@ -setLaravel(new AppDatabaseMigrationStub()); - $migrator->shouldReceive('setConnection')->once()->with(null); - $migrator->shouldReceive('rollback')->twice()->with(false)->andReturn(true, false); - $migrator->shouldReceive('getNotes')->andReturn([]); - - $this->runCommand($command); - } - - - public function testResetCommandCanBePretended() - { - $command = new ResetCommand($migrator = m::mock(Migrator::class)); - $command->setLaravel(new AppDatabaseMigrationStub()); - $migrator->shouldReceive('setConnection')->once()->with('foo'); - $migrator->shouldReceive('rollback')->twice()->with(true)->andReturn(true, false); - $migrator->shouldReceive('getNotes')->andReturn([]); - - $this->runCommand($command, ['--pretend' => true, '--database' => 'foo']); - } - - - protected function runCommand($command, $input = []) - { - return $command->run(new Symfony\Component\Console\Input\ArrayInput($input), new Symfony\Component\Console\Output\NullOutput); - } -} - -class AppDatabaseMigrationStub { - public $env = 'development'; - public function environment() { return $this->env; } -} diff --git a/tests/Database/DatabaseMigrationRollbackCommandTest.php b/tests/Database/DatabaseMigrationRollbackCommandTest.php deleted file mode 100755 index c9fad6a2..00000000 --- a/tests/Database/DatabaseMigrationRollbackCommandTest.php +++ /dev/null @@ -1,51 +0,0 @@ -setLaravel(new AppDatabaseMigrationRollbackStub()); - $migrator->shouldReceive('setConnection')->once()->with(null); - $migrator->shouldReceive('rollback')->once()->with(false); - $migrator->shouldReceive('getNotes')->andReturn([]); - - $this->runCommand($command); - } - - - public function testRollbackCommandCanBePretended() - { - $command = new RollbackCommand($migrator = m::mock(Migrator::class)); - $command->setLaravel(new AppDatabaseMigrationRollbackStub()); - $migrator->shouldReceive('setConnection')->once()->with('foo'); - $migrator->shouldReceive('rollback')->once()->with(true); - $migrator->shouldReceive('getNotes')->andReturn([]); - - $this->runCommand($command, ['--pretend' => true, '--database' => 'foo']); - } - - - protected function runCommand($command, $input = []) - { - return $command->run(new Symfony\Component\Console\Input\ArrayInput($input), new Symfony\Component\Console\Output\NullOutput); - } - -} - -class AppDatabaseMigrationRollbackStub { - public $env = 'development'; - public function environment() { return $this->env; } -} diff --git a/tests/Database/DatabaseMigratorTest.php b/tests/Database/DatabaseMigratorTest.php deleted file mode 100755 index 5b236a5b..00000000 --- a/tests/Database/DatabaseMigratorTest.php +++ /dev/null @@ -1,223 +0,0 @@ -getMock( - Migrator::class, - ['resolve'], - [ - m::mock(MigrationRepositoryInterface::class), - $resolver = m::mock(ConnectionResolverInterface::class), - m::mock(Filesystem::class), - ] - ); - $migrator->getFilesystem()->shouldReceive('glob')->once()->with(__DIR__.'/*_*.php')->andReturn([ - __DIR__.'/2_bar.php', - __DIR__.'/1_foo.php', - __DIR__.'/3_baz.php', - ]); - - $migrator->getFilesystem()->shouldReceive('requireOnce')->with(__DIR__.'/2_bar.php'); - $migrator->getFilesystem()->shouldReceive('requireOnce')->with(__DIR__.'/1_foo.php'); - $migrator->getFilesystem()->shouldReceive('requireOnce')->with(__DIR__.'/3_baz.php'); - - $migrator->getRepository()->shouldReceive('getRan')->once()->andReturn([ - '1_foo', - ]); - $migrator->getRepository()->shouldReceive('getNextBatchNumber')->once()->andReturn(1); - $migrator->getRepository()->shouldReceive('log')->once()->with('2_bar', 1); - $migrator->getRepository()->shouldReceive('log')->once()->with('3_baz', 1); - $barMock = m::mock(stdClass::class); - $barMock->shouldReceive('up')->once(); - $bazMock = m::mock(stdClass::class); - $bazMock->shouldReceive('up')->once(); - - $migrator - ->expects($this->exactly(2)) - ->method('resolve') - ->withConsecutive([$this->equalTo('2_bar')], [$this->equalTo('3_baz')]) - ->willReturnOnConsecutiveCalls($barMock, $bazMock); - - $migrator->run(__DIR__); - } - - - public function testUpMigrationCanBePretended() - { - $migrator = $this->getMock(Migrator::class, ['resolve'], [ - m::mock(MigrationRepositoryInterface::class), - $resolver = m::mock(ConnectionResolverInterface::class), - m::mock(Filesystem::class), - ]); - $migrator->getFilesystem()->shouldReceive('glob')->once()->with(__DIR__.'/*_*.php')->andReturn([ - __DIR__.'/2_bar.php', - __DIR__.'/1_foo.php', - __DIR__.'/3_baz.php', - ]); - $migrator->getFilesystem()->shouldReceive('requireOnce')->with(__DIR__.'/2_bar.php'); - $migrator->getFilesystem()->shouldReceive('requireOnce')->with(__DIR__.'/1_foo.php'); - $migrator->getFilesystem()->shouldReceive('requireOnce')->with(__DIR__.'/3_baz.php'); - $migrator->getRepository()->shouldReceive('getRan')->once()->andReturn([ - '1_foo', - ]); - $migrator->getRepository()->shouldReceive('getNextBatchNumber')->once()->andReturn(1); - - $barMock = m::mock(stdClass::class); - $barMock->shouldReceive('getConnection')->once()->andReturn(null); - $barMock->shouldReceive('up')->once(); - - $bazMock = m::mock(stdClass::class); - $bazMock->shouldReceive('getConnection')->once()->andReturn(null); - $bazMock->shouldReceive('up')->once(); - - $migrator - ->expects($this->exactly(2)) - ->method('resolve') - ->withConsecutive([$this->equalTo('2_bar')], [$this->equalTo('3_baz')]) - ->willReturnOnConsecutiveCalls($barMock, $bazMock); - - $connection = m::mock(stdClass::class); - $connection->shouldReceive('pretend')->with(m::type('Closure'))->andReturnUsing(function($closure) - { - $closure(); - return [['query' => 'foo']]; - }, - function($closure) - { - $closure(); - return [['query' => 'bar']]; - }); - $resolver->shouldReceive('connection')->with(null)->andReturn($connection); - - $migrator->run(__DIR__, true); - } - - - public function testNothingIsDoneWhenNoMigrationsAreOutstanding() - { - $migrator = $this->getMock(Migrator::class, ['resolve'], [ - m::mock(MigrationRepositoryInterface::class), - $resolver = m::mock(ConnectionResolverInterface::class), - m::mock(Filesystem::class), - ]); - $migrator->getFilesystem()->shouldReceive('glob')->once()->with(__DIR__.'/*_*.php')->andReturn([ - __DIR__.'/1_foo.php', - ]); - $migrator->getFilesystem()->shouldReceive('requireOnce')->with(__DIR__.'/1_foo.php'); - $migrator->getRepository()->shouldReceive('getRan')->once()->andReturn([ - '1_foo', - ]); - - $migrator->run(__DIR__); - } - - - public function testLastBatchOfMigrationsCanBeRolledBack() - { - $migrator = $this->getMock(Migrator::class, ['resolve'], [ - m::mock(MigrationRepositoryInterface::class), - $resolver = m::mock(ConnectionResolverInterface::class), - m::mock(Filesystem::class), - ]); - $migrator->getRepository()->shouldReceive('getLast')->once()->andReturn([ - $fooMigration = new MigratorTestMigrationStub('foo'), - $barMigration = new MigratorTestMigrationStub('bar'), - ]); - - $barMock = m::mock(stdClass::class); - $barMock->shouldReceive('down')->once(); - - $fooMock = m::mock(stdClass::class); - $fooMock->shouldReceive('down')->once(); - - $migrator - ->expects($this->exactly(2)) - ->method('resolve') - ->withConsecutive([$this->equalTo('foo')], [$this->equalTo('bar')]) - ->willReturnOnConsecutiveCalls($barMock, $fooMock); - - $migrator->getRepository()->shouldReceive('delete')->once()->with($barMigration); - $migrator->getRepository()->shouldReceive('delete')->once()->with($fooMigration); - - $migrator->rollback(); - } - - - public function testRollbackMigrationsCanBePretended() - { - $migrator = $this->getMock(Migrator::class, ['resolve'], [ - m::mock(MigrationRepositoryInterface::class), - $resolver = m::mock(ConnectionResolverInterface::class), - m::mock(Filesystem::class), - ]); - $migrator->getRepository()->shouldReceive('getLast')->once()->andReturn([ - $fooMigration = new MigratorTestMigrationStub('foo'), - $barMigration = new MigratorTestMigrationStub('bar'), - ]); - - $barMock = m::mock(stdClass::class); - $barMock->shouldReceive('getConnection')->once()->andReturn(null); - $barMock->shouldReceive('down')->once(); - - $fooMock = m::mock(stdClass::class); - $fooMock->shouldReceive('getConnection')->once()->andReturn(null); - $fooMock->shouldReceive('down')->once(); - - $migrator - ->expects($this->exactly(2)) - ->method('resolve') - ->withConsecutive([$this->equalTo('foo')], [$this->equalTo('bar')]) - ->willReturnOnConsecutiveCalls($barMock, $fooMock); - - $connection = m::mock(stdClass::class); - $connection->shouldReceive('pretend')->with(m::type('Closure'))->andReturnUsing(function($closure) - { - $closure(); - return [['query' => 'bar']]; - }, - function($closure) - { - $closure(); - return [['query' => 'foo']]; - }); - $resolver->shouldReceive('connection')->with(null)->andReturn($connection); - - $migrator->rollback(true); - } - - - public function testNothingIsRolledBackWhenNothingInRepository() - { - $migrator = $this->getMock(Migrator::class, ['resolve'], [ - m::mock(MigrationRepositoryInterface::class), - $resolver = m::mock(ConnectionResolverInterface::class), - m::mock(Filesystem::class), - ]); - $migrator->getRepository()->shouldReceive('getLast')->once()->andReturn([]); - - $migrator->rollback(); - } - -} - - -class MigratorTestMigrationStub { - public function __construct($migration) { $this->migration = $migration; } - public $migration; -} diff --git a/tests/Database/DatabaseMySqlProcessorTest.php b/tests/Database/DatabaseMySqlProcessorTest.php deleted file mode 100644 index 2ef41c89..00000000 --- a/tests/Database/DatabaseMySqlProcessorTest.php +++ /dev/null @@ -1,22 +0,0 @@ - 'id'], ['column_name' => 'name'], ['column_name' => 'email']]; - $expected = ['id', 'name', 'email']; - $this->assertEquals($expected, $processor->processColumnListing($listing)); - - // convert listing to objects to simulate PDO::FETCH_CLASS - foreach($listing as &$row) { - $row = (object) $row; - } - - $this->assertEquals($expected, $processor->processColumnListing($listing)); - } - -} diff --git a/tests/Database/DatabaseMySqlSchemaGrammarTest.php b/tests/Database/DatabaseMySqlSchemaGrammarTest.php deleted file mode 100755 index fb852688..00000000 --- a/tests/Database/DatabaseMySqlSchemaGrammarTest.php +++ /dev/null @@ -1,544 +0,0 @@ -create(); - $blueprint->increments('id'); - $blueprint->string('email'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->once()->with('charset')->andReturn('utf8'); - $conn->shouldReceive('getConfig')->once()->with('collation')->andReturn('utf8_unicode_ci'); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create table `users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null) default character set utf8 collate utf8_unicode_ci', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $blueprint->string('email'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); - - $statements = $blueprint->toSql($conn, $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `id` int unsigned not null auto_increment primary key, add `email` varchar(255) not null', $statements[0]); - } - - - public function testBasicCreateTableWithPrefix() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $grammar = $this->getGrammar(); - $grammar->setTablePrefix('prefix_'); - - $conn = $this->getConnection(); - $conn->shouldReceive('getConfig')->andReturn(null); - - $statements = $blueprint->toSql($conn, $grammar); - - $this->assertCount(1, $statements); - $this->assertEquals('create table `prefix_users` (`id` int unsigned not null auto_increment primary key, `email` varchar(255) not null)', $statements[0]); - } - - - public function testDropTable() - { - $blueprint = new Blueprint('users'); - $blueprint->drop(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop table `users`', $statements[0]); - } - - - public function testDropTableIfExists() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIfExists(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop table if exists `users`', $statements[0]); - } - - - public function testDropColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` drop `foo`', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn(['foo', 'bar']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` drop `foo`, drop `bar`', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` drop `foo`, drop `bar`', $statements[0]); - } - - - public function testDropPrimary() - { - $blueprint = new Blueprint('users'); - $blueprint->dropPrimary(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` drop primary key', $statements[0]); - } - - - public function testDropUnique() - { - $blueprint = new Blueprint('users'); - $blueprint->dropUnique('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` drop index foo', $statements[0]); - } - - - public function testDropIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIndex('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` drop index foo', $statements[0]); - } - - - public function testDropForeign() - { - $blueprint = new Blueprint('users'); - $blueprint->dropForeign('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` drop foreign key foo', $statements[0]); - } - - - public function testDropTimestamps() - { - $blueprint = new Blueprint('users'); - $blueprint->dropTimestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` drop `created_at`, drop `updated_at`', $statements[0]); - } - - - public function testRenameTable() - { - $blueprint = new Blueprint('users'); - $blueprint->rename('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('rename table `users` to `foo`', $statements[0]); - } - - - public function testAddingPrimaryKey() - { - $blueprint = new Blueprint('users'); - $blueprint->primary('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add primary key bar(`foo`)', $statements[0]); - } - - - public function testAddingUniqueKey() - { - $blueprint = new Blueprint('users'); - $blueprint->unique('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add unique bar(`foo`)', $statements[0]); - } - - - public function testAddingIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add index baz(`foo`, `bar`)', $statements[0]); - } - - - public function testAddingForeignKey() - { - $blueprint = new Blueprint('users'); - $blueprint->foreign('foo_id')->references('id')->on('orders'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add constraint users_foo_id_foreign foreign key (`foo_id`) references `orders` (`id`)', $statements[0]); - } - - - public function testAddingIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `id` int unsigned not null auto_increment primary key', $statements[0]); - } - - - public function testAddingBigIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->bigIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `id` bigint unsigned not null auto_increment primary key', $statements[0]); - } - - - public function testAddingColumnAfterAnotherColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->string('name')->after('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `name` varchar(255) not null after `foo`', $statements[0]); - } - - - public function testAddingString() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` varchar(255) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` varchar(100) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default('bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` varchar(100) null default \'bar\'', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default(new Illuminate\Database\Query\Expression('CURRENT TIMESTAMP')); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` varchar(100) null default CURRENT TIMESTAMP', $statements[0]); - } - - - public function testAddingText() - { - $blueprint = new Blueprint('users'); - $blueprint->text('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` text not null', $statements[0]); - } - - - public function testAddingBigInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` bigint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` bigint not null auto_increment primary key', $statements[0]); - } - - - public function testAddingInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` int not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` int not null auto_increment primary key', $statements[0]); - } - - - public function testAddingMediumInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` mediumint not null', $statements[0]); - } - - - public function testAddingSmallInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` smallint not null', $statements[0]); - } - - - public function testAddingTinyInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` tinyint not null', $statements[0]); - } - - - public function testAddingFloat() - { - $blueprint = new Blueprint('users'); - $blueprint->float('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` float(5, 2) not null', $statements[0]); - } - - - public function testAddingDouble() - { - $blueprint = new Blueprint('users'); - $blueprint->double('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` double not null', $statements[0]); - } - - - public function testAddingDoubleSpecifyingPrecision() - { - $blueprint = new Blueprint('users'); - $blueprint->double('foo', 15, 8); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` double(15, 8) not null', $statements[0]); - } - - - public function testAddingDecimal() - { - $blueprint = new Blueprint('users'); - $blueprint->decimal('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` decimal(5, 2) not null', $statements[0]); - } - - - public function testAddingBoolean() - { - $blueprint = new Blueprint('users'); - $blueprint->boolean('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` tinyint(1) not null', $statements[0]); - } - - - public function testAddingEnum() - { - $blueprint = new Blueprint('users'); - $blueprint->enum('foo', ['bar', 'baz']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` enum(\'bar\', \'baz\') not null', $statements[0]); - } - - - public function testAddingDate() - { - $blueprint = new Blueprint('users'); - $blueprint->date('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` date not null', $statements[0]); - } - - - public function testAddingDateTime() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` datetime not null', $statements[0]); - } - - - public function testAddingTime() - { - $blueprint = new Blueprint('users'); - $blueprint->time('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` time not null', $statements[0]); - } - - - public function testAddingTimeStamp() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` timestamp default 0 not null', $statements[0]); - } - - - public function testAddingTimeStamps() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `created_at` datetime not null, add `updated_at` datetime not null', $statements[0]); - } - - - public function testAddingTimeStampsWithRealTimestampColumnType() - { - $blueprint = new Blueprint('users'); - $blueprint->timestampsWithTimestampColumnType(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `created_at` timestamp default 0 not null, add `updated_at` timestamp default 0 not null', $statements[0]); - } - - - public function testAddingNullableTimeStamps() - { - $blueprint = new Blueprint('users'); - $blueprint->nullableTimestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `created_at` datetime null, add `updated_at` datetime null', $statements[0]); - } - - - public function testAddingRememberToken() - { - $blueprint = new Blueprint('users'); - $blueprint->rememberToken(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `remember_token` varchar(100) null', $statements[0]); - } - - - public function testAddingBinary() - { - $blueprint = new Blueprint('users'); - $blueprint->binary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table `users` add `foo` blob not null', $statements[0]); - } - - - protected function getConnection() - { - return m::mock(Connection::class); - } - - - public function getGrammar() - { - return new Illuminate\Database\Schema\Grammars\MySqlGrammar; - } - -} diff --git a/tests/Database/DatabasePostgresProcessorTest.php b/tests/Database/DatabasePostgresProcessorTest.php deleted file mode 100644 index abef1d89..00000000 --- a/tests/Database/DatabasePostgresProcessorTest.php +++ /dev/null @@ -1,25 +0,0 @@ - 'id'], ['column_name' => 'name'], ['column_name' => 'email']]; - $expected = ['id', 'name', 'email']; - - $this->assertEquals($expected, $processor->processColumnListing($listing)); - - // convert listing to objects to simulate PDO::FETCH_CLASS - foreach($listing as &$row) - { - $row = (object) $row; - } - - $this->assertEquals($expected, $processor->processColumnListing($listing)); - } - -} diff --git a/tests/Database/DatabasePostgresSchemaGrammarTest.php b/tests/Database/DatabasePostgresSchemaGrammarTest.php deleted file mode 100755 index d616ec68..00000000 --- a/tests/Database/DatabasePostgresSchemaGrammarTest.php +++ /dev/null @@ -1,443 +0,0 @@ -create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create table "users" ("id" serial primary key not null, "email" varchar(255) not null)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "id" serial primary key not null, add column "email" varchar(255) not null', $statements[0]); - } - - - public function testDropTable() - { - $blueprint = new Blueprint('users'); - $blueprint->drop(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop table "users"', $statements[0]); - } - - - public function testDropTableIfExists() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIfExists(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop table if exists "users"', $statements[0]); - } - - - public function testDropColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop column "foo"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn(['foo', 'bar']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop column "foo", drop column "bar"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop column "foo", drop column "bar"', $statements[0]); - } - - - public function testDropPrimary() - { - $blueprint = new Blueprint('users'); - $blueprint->dropPrimary(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop constraint users_pkey', $statements[0]); - } - - - public function testDropUnique() - { - $blueprint = new Blueprint('users'); - $blueprint->dropUnique('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop constraint foo', $statements[0]); - } - - - public function testDropIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIndex('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop index foo', $statements[0]); - } - - - public function testDropForeign() - { - $blueprint = new Blueprint('users'); - $blueprint->dropForeign('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop constraint foo', $statements[0]); - } - - - public function testDropTimestamps() - { - $blueprint = new Blueprint('users'); - $blueprint->dropTimestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop column "created_at", drop column "updated_at"', $statements[0]); - } - - - public function testRenameTable() - { - $blueprint = new Blueprint('users'); - $blueprint->rename('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" rename to "foo"', $statements[0]); - } - - - public function testAddingPrimaryKey() - { - $blueprint = new Blueprint('users'); - $blueprint->primary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add primary key ("foo")', $statements[0]); - } - - - public function testAddingUniqueKey() - { - $blueprint = new Blueprint('users'); - $blueprint->unique('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add constraint bar unique ("foo")', $statements[0]); - } - - - public function testAddingIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create index baz on "users" ("foo", "bar")', $statements[0]); - } - - - public function testAddingIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "id" serial primary key not null', $statements[0]); - } - - - public function testAddingBigIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->bigIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "id" bigserial primary key not null', $statements[0]); - } - - - public function testAddingString() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" varchar(255) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" varchar(100) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default('bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" varchar(100) null default \'bar\'', $statements[0]); - } - - - public function testAddingText() - { - $blueprint = new Blueprint('users'); - $blueprint->text('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" text not null', $statements[0]); - } - - - public function testAddingBigInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" bigint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" bigserial primary key not null', $statements[0]); - } - - - public function testAddingInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" serial primary key not null', $statements[0]); - } - - - public function testAddingMediumInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" integer not null', $statements[0]); - } - - - public function testAddingTinyInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" smallint not null', $statements[0]); - } - - - public function testAddingSmallInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" smallint not null', $statements[0]); - } - - - public function testAddingFloat() - { - $blueprint = new Blueprint('users'); - $blueprint->float('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" real not null', $statements[0]); - } - - - public function testAddingDouble() - { - $blueprint = new Blueprint('users'); - $blueprint->double('foo', 15, 8); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" double precision not null', $statements[0]); - } - - - public function testAddingDecimal() - { - $blueprint = new Blueprint('users'); - $blueprint->decimal('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" decimal(5, 2) not null', $statements[0]); - } - - - public function testAddingBoolean() - { - $blueprint = new Blueprint('users'); - $blueprint->boolean('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" boolean not null', $statements[0]); - } - - - public function testAddingEnum() - { - $blueprint = new Blueprint('users'); - $blueprint->enum('foo', ['bar', 'baz']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" varchar(255) check ("foo" in (\'bar\', \'baz\')) not null', $statements[0]); - } - - - public function testAddingDate() - { - $blueprint = new Blueprint('users'); - $blueprint->date('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" date not null', $statements[0]); - } - - - public function testAddingDateTime() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" timestamp not null', $statements[0]); - } - - - public function testAddingTime() - { - $blueprint = new Blueprint('users'); - $blueprint->time('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" time not null', $statements[0]); - } - - - public function testAddingTimeStamp() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" timestamp not null', $statements[0]); - } - - - public function testAddingTimeStamps() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "created_at" timestamp not null, add column "updated_at" timestamp not null', $statements[0]); - } - - - public function testAddingBinary() - { - $blueprint = new Blueprint('users'); - $blueprint->binary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" bytea not null', $statements[0]); - } - - - protected function getConnection() - { - return m::mock(Connection::class); - } - - - public function getGrammar() - { - return new Illuminate\Database\Schema\Grammars\PostgresGrammar; - } - -} diff --git a/tests/Database/DatabaseProcessorTest.php b/tests/Database/DatabaseProcessorTest.php deleted file mode 100755 index 9575845f..00000000 --- a/tests/Database/DatabaseProcessorTest.php +++ /dev/null @@ -1,43 +0,0 @@ -createMock(ProcessorTestPDOStub::class); - $pdo->expects($this->once())->method('lastInsertId')->with($this->equalTo('id'))->willReturn('1'); - $connection = m::mock(Connection::class); - $connection->shouldReceive('insert')->once()->with('sql', ['foo']); - $connection->shouldReceive('getPdo')->once()->andReturn($pdo); - $builder = m::mock(Builder::class); - $builder->shouldReceive('getConnection')->andReturn($connection); - $processor = new Illuminate\Database\Query\Processors\Processor; - $result = $processor->processInsertGetId($builder, 'sql', ['foo'], 'id'); - $this->assertSame(1, $result); - } - -} - -class ProcessorTestPDOStub extends PDO { - - public function __construct() { - // - } - - public function lastInsertId($sequence = null): string|false { - // - } - -} diff --git a/tests/Database/DatabaseQueryBuilderTest.php b/tests/Database/DatabaseQueryBuilderTest.php deleted file mode 100755 index ffc73c47..00000000 --- a/tests/Database/DatabaseQueryBuilderTest.php +++ /dev/null @@ -1,1530 +0,0 @@ -getBuilder(); - $builder->select('*')->from('users'); - $this->assertEquals('select * from "users"', $builder->toSql()); - } - - - public function testBasicSelectUseWritePdo(): void - { - $builder = $this->getMySqlBuilderWithProcessor(); - $builder->getConnection()->shouldReceive('select')->once() - ->with('select * from `users`', [], false); - $builder->useWritePdo()->select('*')->from('users')->get(); - - $builder = $this->getMySqlBuilderWithProcessor(); - $builder->getConnection()->shouldReceive('select')->once() - ->with('select * from `users`', []); - $builder->select('*')->from('users')->get(); - } - - - public function testBasicTableWrappingProtectsQuotationMarks(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('some"table'); - $this->assertEquals('select * from "some""table"', $builder->toSql()); - } - - public function testAliasWrappingAsWholeConstant(): void - { - $builder = $this->getBuilder(); - $builder->select('x.y as foo.bar')->from('baz'); - $this->assertEquals('select "x"."y" as "foo.bar" from "baz"', $builder->toSql()); - } - - public function testAddingSelects(): void - { - $builder = $this->getBuilder(); - $builder->select('foo')->addSelect('bar')->addSelect(['baz', 'boom'])->from('users'); - $this->assertEquals('select "foo", "bar", "baz", "boom" from "users"', $builder->toSql()); - } - - - public function testBasicSelectWithPrefix(): void - { - $builder = $this->getBuilder(); - $builder->getGrammar()->setTablePrefix('prefix_'); - $builder->select('*')->from('users'); - $this->assertEquals('select * from "prefix_users"', $builder->toSql()); - } - - - public function testBasicSelectDistinct(): void - { - $builder = $this->getBuilder(); - $builder->distinct()->select('foo', 'bar')->from('users'); - $this->assertEquals('select distinct "foo", "bar" from "users"', $builder->toSql()); - } - - - public function testSelectWithCaching(): void - { - $cache = m::mock('stdClass'); - $driver = m::mock('stdClass'); - $query = $this->setupCacheTestQuery($cache, $driver); - - $query = $query->remember(5); - - $driver->shouldReceive('remember') - ->once() - ->with($query->getCacheKey(), m::type(\DateTimeInterface::class), m::type('Closure')) - ->andReturnUsing(function($key, $minutes, $callback) { return $callback(); }); - - - $this->assertEquals($query->get(), ['results']); - } - - - public function testSelectWithCachingForever(): void - { - $cache = m::mock('stdClass'); - $driver = m::mock('stdClass'); - $query = $this->setupCacheTestQuery($cache, $driver); - - $query = $query->rememberForever(); - - $driver->shouldReceive('rememberForever') - ->once() - ->with($query->getCacheKey(), m::type('Closure')) - ->andReturnUsing(function($key, $callback) { return $callback(); }); - - - - $this->assertEquals($query->get(), ['results']); - } - - - public function testSelectWithCachingAndTags(): void - { - $taggedCache = m::mock('StdClass'); - $cache = m::mock('stdClass'); - $driver = m::mock('stdClass'); - - $driver->shouldReceive('tags') - ->once() - ->with(['foo','bar']) - ->andReturn($taggedCache); - - $query = $this->setupCacheTestQuery($cache, $driver); - $query = $query->cacheTags(['foo', 'bar'])->remember(5); - - $taggedCache->shouldReceive('remember') - ->once() - ->with($query->getCacheKey(), m::type(\DateTimeInterface::class), m::type('Closure')) - ->andReturnUsing(function($key, $minutes, $callback) { return $callback(); }); - - $this->assertEquals($query->get(), ['results']); - } - - - public function testBasicAlias(): void - { - $builder = $this->getBuilder(); - $builder->select('foo as bar')->from('users'); - $this->assertEquals('select "foo" as "bar" from "users"', $builder->toSql()); - } - - - public function testBasicTableWrapping(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('public.users'); - $this->assertEquals('select * from "public"."users"', $builder->toSql()); - } - - - public function testBasicWheres(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1); - $this->assertEquals('select * from "users" where "id" = ?', $builder->toSql()); - $this->assertEquals([0 => 1], $builder->getBindings()); - } - - - public function testMySqlWrappingProtectsQuotationMarks(): void - { - $builder = $this->getMySqlBuilder(); - $builder->select('*')->From('some`table'); - $this->assertEquals('select * from `some``table`', $builder->toSql()); - } - - - public function testWhereDayMySql(): void - { - $builder = $this->getMySqlBuilder(); - $builder->select('*')->from('users')->whereDay('created_at', '=', 1); - $this->assertEquals('select * from `users` where day(`created_at`) = ?', $builder->toSql()); - $this->assertEquals([0 => 1], $builder->getBindings()); - } - - - public function testWhereMonthMySql(): void - { - $builder = $this->getMySqlBuilder(); - $builder->select('*')->from('users')->whereMonth('created_at', '=', 5); - $this->assertEquals('select * from `users` where month(`created_at`) = ?', $builder->toSql()); - $this->assertEquals([0 => 5], $builder->getBindings()); - } - - - public function testWhereYearMySql(): void - { - $builder = $this->getMySqlBuilder(); - $builder->select('*')->from('users')->whereYear('created_at', '=', 2014); - $this->assertEquals('select * from `users` where year(`created_at`) = ?', $builder->toSql()); - $this->assertEquals([0 => 2014], $builder->getBindings()); - } - - - public function testWhereDayPostgres(): void - { - $builder = $this->getPostgresBuilder(); - $builder->select('*')->from('users')->whereDay('created_at', '=', 1); - $this->assertEquals('select * from "users" where day("created_at") = ?', $builder->toSql()); - $this->assertEquals([0 => 1], $builder->getBindings()); - } - - - public function testWhereMonthPostgres(): void - { - $builder = $this->getPostgresBuilder(); - $builder->select('*')->from('users')->whereMonth('created_at', '=', 5); - $this->assertEquals('select * from "users" where month("created_at") = ?', $builder->toSql()); - $this->assertEquals([0 => 5], $builder->getBindings()); - } - - - public function testWhereYearPostgres(): void - { - $builder = $this->getPostgresBuilder(); - $builder->select('*')->from('users')->whereYear('created_at', '=', 2014); - $this->assertEquals('select * from "users" where year("created_at") = ?', $builder->toSql()); - $this->assertEquals([0 => 2014], $builder->getBindings()); - } - - - public function testWhereDaySqlite(): void - { - $builder = $this->getSQLiteBuilder(); - $builder->select('*')->from('users')->whereDay('created_at', '=', 1); - $this->assertEquals('select * from "users" where strftime(\'%d\', "created_at") = ?', $builder->toSql()); - $this->assertEquals([0 => 1], $builder->getBindings()); - } - - - public function testWhereMonthSqlite(): void - { - $builder = $this->getSQLiteBuilder(); - $builder->select('*')->from('users')->whereMonth('created_at', '=', 5); - $this->assertEquals('select * from "users" where strftime(\'%m\', "created_at") = ?', $builder->toSql()); - $this->assertEquals([0 => 5], $builder->getBindings()); - } - - - public function testWhereYearSqlite(): void - { - $builder = $this->getSQLiteBuilder(); - $builder->select('*')->from('users')->whereYear('created_at', '=', 2014); - $this->assertEquals('select * from "users" where strftime(\'%Y\', "created_at") = ?', $builder->toSql()); - $this->assertEquals([0 => 2014], $builder->getBindings()); - } - - - public function testWhereDaySqlServer(): void - { - $builder = $this->getPostgresBuilder(); - $builder->select('*')->from('users')->whereDay('created_at', '=', 1); - $this->assertEquals('select * from "users" where day("created_at") = ?', $builder->toSql()); - $this->assertEquals([0 => 1], $builder->getBindings()); - } - - - public function testWhereMonthSqlServer(): void - { - $builder = $this->getPostgresBuilder(); - $builder->select('*')->from('users')->whereMonth('created_at', '=', 5); - $this->assertEquals('select * from "users" where month("created_at") = ?', $builder->toSql()); - $this->assertEquals([0 => 5], $builder->getBindings()); - } - - - public function testWhereYearSqlServer(): void - { - $builder = $this->getPostgresBuilder(); - $builder->select('*')->from('users')->whereYear('created_at', '=', 2014); - $this->assertEquals('select * from "users" where year("created_at") = ?', $builder->toSql()); - $this->assertEquals([0 => 2014], $builder->getBindings()); - } - - - public function testWhereBetweens(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereBetween('id', [1, 2]); - $this->assertEquals('select * from "users" where "id" between ? and ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereNotBetween('id', [1, 2]); - $this->assertEquals('select * from "users" where "id" not between ? and ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - } - - - public function testBasicOrWheres(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1)->orWhere('email', '=', 'foo'); - $this->assertEquals('select * from "users" where "id" = ? or "email" = ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 'foo'], $builder->getBindings()); - } - - - public function testRawWheres(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereRaw('id = ? or email = ?', [1, 'foo']); - $this->assertEquals('select * from "users" where id = ? or email = ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 'foo'], $builder->getBindings()); - } - - - public function testRawOrWheres(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1)->orWhereRaw('email = ?', ['foo']); - $this->assertEquals('select * from "users" where "id" = ? or email = ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 'foo'], $builder->getBindings()); - } - - - public function testBasicWhereIns(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereIn('id', [1, 2, 3]); - $this->assertEquals('select * from "users" where "id" in (?, ?, ?)', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2, 2 => 3], $builder->getBindings()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1)->orWhereIn('id', [1, 2, 3]); - $this->assertEquals('select * from "users" where "id" = ? or "id" in (?, ?, ?)', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 1, 2 => 2, 3 => 3], $builder->getBindings()); - } - - - public function testBasicWhereNotIns(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereNotIn('id', [1, 2, 3]); - $this->assertEquals('select * from "users" where "id" not in (?, ?, ?)', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2, 2 => 3], $builder->getBindings()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1)->orWhereNotIn('id', [1, 2, 3]); - $this->assertEquals('select * from "users" where "id" = ? or "id" not in (?, ?, ?)', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 1, 2 => 2, 3 => 3], $builder->getBindings()); - } - - - public function testEmptyWhereIns(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereIn('id', []); - $this->assertEquals('select * from "users" where 0 = 1', $builder->toSql()); - $this->assertEquals([], $builder->getBindings()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1)->orWhereIn('id', []); - $this->assertEquals('select * from "users" where "id" = ? or 0 = 1', $builder->toSql()); - $this->assertEquals([0 => 1], $builder->getBindings()); - } - - - public function testEmptyWhereNotIns(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereNotIn('id', []); - $this->assertEquals('select * from "users" where 1 = 1', $builder->toSql()); - $this->assertEquals([], $builder->getBindings()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1)->orWhereNotIn('id', []); - $this->assertEquals('select * from "users" where "id" = ? or 1 = 1', $builder->toSql()); - $this->assertEquals([0 => 1], $builder->getBindings()); - } - - - public function testUnions(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1); - $builder->union($this->getBuilder()->select('*')->from('users')->where('id', '=', 2)); - $this->assertEquals('select * from "users" where "id" = ? union select * from "users" where "id" = ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - - $builder = $this->getMySqlBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1); - $builder->union($this->getMySqlBuilder()->select('*')->from('users')->where('id', '=', 2)); - $this->assertEquals('(select * from `users` where `id` = ?) union (select * from `users` where `id` = ?)', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - } - - - public function testUnionAlls(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1); - $builder->unionAll($this->getBuilder()->select('*')->from('users')->where('id', '=', 2)); - $this->assertEquals('select * from "users" where "id" = ? union all select * from "users" where "id" = ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - } - - - public function testMultipleUnions(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1); - $builder->union($this->getBuilder()->select('*')->from('users')->where('id', '=', 2)); - $builder->union($this->getBuilder()->select('*')->from('users')->where('id', '=', 3)); - $this->assertEquals('select * from "users" where "id" = ? union select * from "users" where "id" = ? union select * from "users" where "id" = ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2, 2 => 3], $builder->getBindings()); - } - - - public function testMultipleUnionAlls(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1); - $builder->unionAll($this->getBuilder()->select('*')->from('users')->where('id', '=', 2)); - $builder->unionAll($this->getBuilder()->select('*')->from('users')->where('id', '=', 3)); - $this->assertEquals('select * from "users" where "id" = ? union all select * from "users" where "id" = ? union all select * from "users" where "id" = ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2, 2 => 3], $builder->getBindings()); - } - - - public function testUnionOrderBys(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1); - $builder->union($this->getBuilder()->select('*')->from('users')->where('id', '=', 2)); - $builder->orderBy('id', 'desc'); - $this->assertEquals('select * from "users" where "id" = ? union select * from "users" where "id" = ? order by "id" desc', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - } - - - public function testUnionLimitsAndOffsets(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users'); - $builder->union($this->getBuilder()->select('*')->from('dogs')); - $builder->skip(5)->take(10); - $this->assertEquals('select * from "users" union select * from "dogs" limit 10 offset 5', $builder->toSql()); - } - - - public function testMySqlUnionOrderBys(): void - { - $builder = $this->getMySqlBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1); - $builder->union($this->getMySqlBuilder()->select('*')->from('users')->where('id', '=', 2)); - $builder->orderBy('id', 'desc'); - $this->assertEquals('(select * from `users` where `id` = ?) union (select * from `users` where `id` = ?) order by `id` desc', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 2], $builder->getBindings()); - } - - - public function testMySqlUnionLimitsAndOffsets(): void - { - $builder = $this->getMySqlBuilder(); - $builder->select('*')->from('users'); - $builder->union($this->getMySqlBuilder()->select('*')->from('dogs')); - $builder->skip(5)->take(10); - $this->assertEquals('(select * from `users`) union (select * from `dogs`) limit 10 offset 5', $builder->toSql()); - } - - - public function testSubSelectWhereIns(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereIn('id', function($q) - { - $q->select('id')->from('users')->where('age', '>', 25)->take(3); - }); - $this->assertEquals('select * from "users" where "id" in (select "id" from "users" where "age" > ? limit 3)', $builder->toSql()); - $this->assertEquals([25], $builder->getBindings()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereNotIn('id', function($q) - { - $q->select('id')->from('users')->where('age', '>', 25)->take(3); - }); - $this->assertEquals('select * from "users" where "id" not in (select "id" from "users" where "age" > ? limit 3)', $builder->toSql()); - $this->assertEquals([25], $builder->getBindings()); - } - - - public function testBasicWhereNulls(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereNull('id'); - $this->assertEquals('select * from "users" where "id" is null', $builder->toSql()); - $this->assertEquals([], $builder->getBindings()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '=', 1)->orWhereNull('id'); - $this->assertEquals('select * from "users" where "id" = ? or "id" is null', $builder->toSql()); - $this->assertEquals([0 => 1], $builder->getBindings()); - } - - - public function testBasicWhereNotNulls(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->whereNotNull('id'); - $this->assertEquals('select * from "users" where "id" is not null', $builder->toSql()); - $this->assertEquals([], $builder->getBindings()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', '>', 1)->orWhereNotNull('id'); - $this->assertEquals('select * from "users" where "id" > ? or "id" is not null', $builder->toSql()); - $this->assertEquals([0 => 1], $builder->getBindings()); - } - - - public function testGroupBys(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->groupBy('id', 'email'); - $this->assertEquals('select * from "users" group by "id", "email"', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->groupBy(['id', 'email']); - $this->assertEquals('select * from "users" group by "id", "email"', $builder->toSql()); - } - - - public function testOrderBys(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->orderBy('email')->orderBy('age', 'desc'); - $this->assertEquals('select * from "users" order by "email" asc, "age" desc', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->orderBy('email')->orderByRaw('"age" ? desc', ['foo']); - $this->assertEquals('select * from "users" order by "email" asc, "age" ? desc', $builder->toSql()); - $this->assertEquals(['foo'], $builder->getBindings()); - } - - - public function testHavings(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->having('email', '>', 1); - $this->assertEquals('select * from "users" having "email" > ?', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users') - ->orHaving('email', '=', 'test@example.com') - ->orHaving('email', '=', 'test2@example.com'); - $this->assertEquals('select * from "users" having "email" = ? or "email" = ?', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->groupBy('email')->having('email', '>', 1); - $this->assertEquals('select * from "users" group by "email" having "email" > ?', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('email as foo_email')->from('users')->having('foo_email', '>', 1); - $this->assertEquals('select "email" as "foo_email" from "users" having "foo_email" > ?', $builder->toSql()); - } - - - public function testRawHavings(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->havingRaw('user_foo < user_bar'); - $this->assertEquals('select * from "users" having user_foo < user_bar', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->having('baz', '=', 1)->orHavingRaw('user_foo < user_bar'); - $this->assertEquals('select * from "users" having "baz" = ? or user_foo < user_bar', $builder->toSql()); - } - - - public function testLimitsAndOffsets(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->offset(5)->limit(10); - $this->assertEquals('select * from "users" limit 10 offset 5', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->skip(5)->take(10); - $this->assertEquals('select * from "users" limit 10 offset 5', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->skip(-5)->take(10); - $this->assertEquals('select * from "users" limit 10 offset 0', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->forPage(2, 15); - $this->assertEquals('select * from "users" limit 15 offset 15', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->forPage(-2, 15); - $this->assertEquals('select * from "users" limit 15 offset 0', $builder->toSql()); - } - - - public function testWhereShortcut(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('id', 1)->orWhere('name', 'foo'); - $this->assertEquals('select * from "users" where "id" = ? or "name" = ?', $builder->toSql()); - $this->assertEquals([0 => 1, 1 => 'foo'], $builder->getBindings()); - } - - - public function testNestedWheres(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('email', '=', 'foo')->orWhere(function($q) - { - $q->where('name', '=', 'bar')->where('age', '=', 25); - }); - $this->assertEquals('select * from "users" where "email" = ? or ("name" = ? and "age" = ?)', $builder->toSql()); - $this->assertEquals([0 => 'foo', 1 => 'bar', 2 => 25], $builder->getBindings()); - } - - - public function testFullSubSelects(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('email', '=', 'foo')->orWhere('id', '=', function($q) - { - $q->select(new Raw('max(id)'))->from('users')->where('email', '=', 'bar'); - }); - - $this->assertEquals('select * from "users" where "email" = ? or "id" = (select max(id) from "users" where "email" = ?)', $builder->toSql()); - $this->assertEquals([0 => 'foo', 1 => 'bar'], $builder->getBindings()); - } - - - public function testWhereExists(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('orders')->whereExists(function($q) - { - $q->select('*')->from('products')->where('products.id', '=', new Raw('"orders"."id"')); - }); - $this->assertEquals('select * from "orders" where exists (select * from "products" where "products"."id" = "orders"."id")', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('orders')->whereNotExists(function($q) - { - $q->select('*')->from('products')->where('products.id', '=', new Raw('"orders"."id"')); - }); - $this->assertEquals('select * from "orders" where not exists (select * from "products" where "products"."id" = "orders"."id")', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('orders')->where('id', '=', 1)->orWhereExists(function($q) - { - $q->select('*')->from('products')->where('products.id', '=', new Raw('"orders"."id"')); - }); - $this->assertEquals('select * from "orders" where "id" = ? or exists (select * from "products" where "products"."id" = "orders"."id")', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('orders')->where('id', '=', 1)->orWhereNotExists(function($q) - { - $q->select('*')->from('products')->where('products.id', '=', new Raw('"orders"."id"')); - }); - $this->assertEquals('select * from "orders" where "id" = ? or not exists (select * from "products" where "products"."id" = "orders"."id")', $builder->toSql()); - } - - - public function testBasicJoins(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->join('contacts', 'users.id', '=', 'contacts.id')->leftJoin('photos', 'users.id', '=', 'photos.id'); - $this->assertEquals('select * from "users" inner join "contacts" on "users"."id" = "contacts"."id" left join "photos" on "users"."id" = "photos"."id"', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->leftJoinWhere('photos', 'users.id', '=', 'bar')->joinWhere('photos', 'users.id', '=', 'foo'); - $this->assertEquals('select * from "users" left join "photos" on "users"."id" = ? inner join "photos" on "users"."id" = ?', $builder->toSql()); - $this->assertEquals(['bar', 'foo'], $builder->getBindings()); - } - - - public function testComplexJoin(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->join('contacts', function($j) - { - $j->on('users.id', '=', 'contacts.id')->orOn('users.name', '=', 'contacts.name'); - }); - $this->assertEquals('select * from "users" inner join "contacts" on "users"."id" = "contacts"."id" or "users"."name" = "contacts"."name"', $builder->toSql()); - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->join('contacts', function($j) - { - $j->where('users.id', '=', 'foo')->orWhere('users.name', '=', 'bar'); - }); - $this->assertEquals('select * from "users" inner join "contacts" on "users"."id" = ? or "users"."name" = ?', $builder->toSql()); - $this->assertEquals(['foo', 'bar'], $builder->getBindings()); - - // Run the assertions again - $this->assertEquals('select * from "users" inner join "contacts" on "users"."id" = ? or "users"."name" = ?', $builder->toSql()); - $this->assertEquals(['foo', 'bar'], $builder->getBindings()); - } - - public function testJoinWhereNull(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->join('contacts', function($j) - { - $j->on('users.id', '=', 'contacts.id')->whereNull('contacts.deleted_at'); - }); - $this->assertEquals('select * from "users" inner join "contacts" on "users"."id" = "contacts"."id" and "contacts"."deleted_at" is null', $builder->toSql()); - } - - public function testRawExpressionsInSelect(): void - { - $builder = $this->getBuilder(); - $builder->select(new Raw('substr(foo, 6)'))->from('users'); - $this->assertEquals('select substr(foo, 6) from "users"', $builder->toSql()); - } - - - public function testFindReturnsFirstResultByID(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select * from "users" where "id" = ? limit 1', [1] - )->andReturn([['foo' => 'bar']]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->with($builder, [['foo' => 'bar']])->andReturnUsing(function($query, $results) { return $results; }); - $results = $builder->from('users')->find(1); - $this->assertEquals(['foo' => 'bar'], $results); - } - - - public function testFirstMethodReturnsFirstResult(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select * from "users" where "id" = ? limit 1', [1] - )->andReturn([['foo' => 'bar']]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->with($builder, [['foo' => 'bar']])->andReturnUsing(function($query, $results) { return $results; }); - $results = $builder->from('users')->where('id', '=', 1)->first(); - $this->assertEquals(['foo' => 'bar'], $results); - } - - - public function testListMethodsGetsArrayOfColumnValues(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->andReturn([['foo' => 'bar'], ['foo' => 'baz']]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->with($builder, [['foo' => 'bar'], ['foo' => 'baz']] - )->andReturnUsing(function($query, $results) - { - return $results; - }); - $results = $builder->from('users')->where('id', '=', 1)->pluck('foo'); - $this->assertEquals(['bar', 'baz'], $results); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->andReturn( - [['id' => 1, 'foo' => 'bar'], ['id' => 10, 'foo' => 'baz']] - ); - $builder->getProcessor()->shouldReceive('processSelect')->once()->with($builder, [['id' => 1, 'foo' => 'bar'], ['id' => 10, 'foo' => 'baz']] - )->andReturnUsing(function($query, $results) - { - return $results; - }); - $results = $builder->from('users')->where('id', '=', 1)->pluck('foo', 'id'); - $this->assertEquals([1 => 'bar', 10 => 'baz'], $results); - } - - - public function testImplode(): void - { - // Test without glue. - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->andReturn([['foo' => 'bar'], ['foo' => 'baz']]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->with($builder, [['foo' => 'bar'], ['foo' => 'baz']] - )->andReturnUsing(function($query, $results) - { - return $results; - }); - $results = $builder->from('users')->where('id', '=', 1)->implode('foo'); - $this->assertEquals('barbaz', $results); - - // Test with glue. - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->andReturn([['foo' => 'bar'], ['foo' => 'baz']]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->with($builder, [['foo' => 'bar'], ['foo' => 'baz']] - )->andReturnUsing(function($query, $results) - { - return $results; - }); - $results = $builder->from('users')->where('id', '=', 1)->implode('foo', ','); - $this->assertEquals('bar,baz', $results); - } - - - public function testPaginateCorrectlyCreatesPaginatorInstance(): void - { - $connection = m::mock(ConnectionInterface::class); - $grammar = m::mock(Grammar::class); - $processor = m::mock(Processor::class); - $builder = $this->getMock(Builder::class, ['getPaginationCount', 'forPage', 'get'], [$connection, $grammar, $processor] - ); - $paginator = m::mock(Factory::class); - $paginator->shouldReceive('getCurrentPage')->once()->andReturn(1); - $connection->shouldReceive('getPaginator')->once()->andReturn($paginator); - $builder->expects($this->once())->method('forPage')->with($this->equalTo(1), $this->equalTo(15))->willReturn( - $builder - ); - $builder->expects($this->once())->method('get')->with($this->equalTo(['*']))->willReturn(['foo']); - $builder->expects($this->once())->method('getPaginationCount')->willReturn(10); - $paginator->shouldReceive('make')->once()->with(['foo'], 10, 15)->andReturn(['results']); - - $this->assertEquals(['results'], $builder->paginate(15, ['*'])); - } - - - public function testPaginateCorrectlyCreatesPaginatorInstanceForGroupedQuery(): void - { - $connection = m::mock(ConnectionInterface::class); - $grammar = m::mock(Grammar::class); - $processor = m::mock(Processor::class); - $builder = $this->getMock(Builder::class, ['get'], [$connection, $grammar, $processor]); - $paginator = m::mock(Factory::class); - $paginator->shouldReceive('getCurrentPage')->once()->andReturn(2); - $connection->shouldReceive('getPaginator')->once()->andReturn($paginator); - $builder->expects($this->once())->method('get')->with($this->equalTo(['*']))->willReturn( - ['foo', 'bar', 'baz'] - ); - $paginator->shouldReceive('make')->once()->with(['baz'], 3, 2)->andReturn(['results']); - - $this->assertEquals(['results'], $builder->groupBy('foo')->paginate(2, ['*'])); - } - - - public function testGetPaginationCountGetsResultCount(): void - { - unset($_SERVER['orders']); - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select count(*) as aggregate from "users"', [] - )->andReturn([['aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(function($query, $results) - { - $_SERVER['orders'] = $query->orders; - return $results; - }); - $results = $builder->from('users')->orderBy('foo', 'desc')->getPaginationCount(); - - $this->assertNull($_SERVER['orders']); - unset($_SERVER['orders']); - - $this->assertEquals([0 => ['column' => 'foo', 'direction' => 'desc']], $builder->orders); - $this->assertEquals(1, $results); - } - - - public function testQuickPaginateCorrectlyCreatesPaginatorInstance(): void - { - $connection = m::mock(ConnectionInterface::class); - $grammar = m::mock(Grammar::class); - $processor = m::mock(Processor::class); - $builder = $this->getMock(Builder::class, ['skip', 'take', 'get'], [$connection, $grammar, $processor]); - $paginator = m::mock(Factory::class); - $paginator->shouldReceive('getCurrentPage')->once()->andReturn(1); - $connection->shouldReceive('getPaginator')->once()->andReturn($paginator); - $builder->expects($this->once())->method('skip')->with($this->equalTo(0))->willReturn($builder); - $builder->expects($this->once())->method('take')->with($this->equalTo(16))->willReturn($builder); - $builder->expects($this->once())->method('get')->with($this->equalTo(['*']))->willReturn(['foo']); - $paginator->shouldReceive('make')->once()->with(['foo'], 15)->andReturn(['results']); - - $this->assertEquals(['results'], $builder->simplePaginate(15, ['*'])); - } - - -public function testValueMethodReturnsSingleColumn(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select "foo" from "users" where "id" = ? limit 1', [1] - )->andReturn([['foo' => 'bar']]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->with($builder, [['foo' => 'bar']])->andReturn( - [['foo' => 'bar']] - ); - $results = $builder->from('users')->where('id', '=', 1)->value('foo'); - $this->assertEquals('bar', $results); - } - - - public function testAggregateFunctions(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select count(*) as aggregate from "users"', [] - )->andReturn([['aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(function($builder, $results) { return $results; }); - $results = $builder->from('users')->count(); - $this->assertEquals(1, $results); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select count(*) as aggregate from "users" limit 1', [] - )->andReturn([['aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(function($builder, $results) { return $results; }); - $results = $builder->from('users')->exists(); - $this->assertTrue($results); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select max("id") as aggregate from "users"', [] - )->andReturn([['aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(function($builder, $results) { return $results; }); - $results = $builder->from('users')->max('id'); - $this->assertEquals(1, $results); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select min("id") as aggregate from "users"', [] - )->andReturn([['aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(function($builder, $results) { return $results; }); - $results = $builder->from('users')->min('id'); - $this->assertEquals(1, $results); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select sum("id") as aggregate from "users"', [] - )->andReturn([['aggregate' => 1]]); - $builder->getProcessor()->shouldReceive('processSelect')->once()->andReturnUsing(function($builder, $results) { return $results; }); - $results = $builder->from('users')->sum('id'); - $this->assertEquals(1, $results); - } - - - public function testAggregateResetFollowedByGet(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select count(*) as aggregate from "users"', [] - )->andReturn([['aggregate' => 1]]); - $builder->getConnection()->shouldReceive('select')->once()->with('select sum("id") as aggregate from "users"', [] - )->andReturn([['aggregate' => 2]]); - $builder->getConnection()->shouldReceive('select')->once()->with('select "column1", "column2" from "users"', [])->andReturn( - [['column1' => 'foo', 'column2' => 'bar']] - ); - $builder->getProcessor()->shouldReceive('processSelect')->andReturnUsing(function($builder, $results) { return $results; }); - $builder->from('users')->select('column1', 'column2'); - $count = $builder->count(); - $this->assertEquals(1, $count); - $sum = $builder->sum('id'); - $this->assertEquals(2, $sum); - $result = $builder->get(); - $this->assertEquals([['column1' => 'foo', 'column2' => 'bar']], $result); - } - - - public function testAggregateResetFollowedBySelectGet(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select count("column1") as aggregate from "users"', [] - )->andReturn([['aggregate' => 1]]); - $builder->getConnection()->shouldReceive('select')->once()->with('select "column2", "column3" from "users"', [])->andReturn( - [['column2' => 'foo', 'column3' => 'bar']] - ); - $builder->getProcessor()->shouldReceive('processSelect')->andReturnUsing(function($builder, $results) { return $results; }); - $builder->from('users'); - $count = $builder->count('column1'); - $this->assertEquals(1, $count); - $result = $builder->select('column2', 'column3')->get(); - $this->assertEquals([['column2' => 'foo', 'column3' => 'bar']], $result); - } - - - public function testAggregateResetFollowedByGetWithColumns(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('select')->once()->with('select count("column1") as aggregate from "users"', [] - )->andReturn([['aggregate' => 1]]); - $builder->getConnection()->shouldReceive('select')->once()->with('select "column2", "column3" from "users"', [])->andReturn( - [['column2' => 'foo', 'column3' => 'bar']] - ); - $builder->getProcessor()->shouldReceive('processSelect')->andReturnUsing(function($builder, $results) { return $results; }); - $builder->from('users'); - $count = $builder->count('column1'); - $this->assertEquals(1, $count); - $result = $builder->get(['column2', 'column3']); - $this->assertEquals([['column2' => 'foo', 'column3' => 'bar']], $result); - } - - - public function testInsertMethod(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('insert')->once()->with('insert into "users" ("email") values (?)', ['foo'] - )->andReturn(true); - $result = $builder->from('users')->insert(['email' => 'foo']); - $this->assertTrue($result); - } - - - public function testSQLiteMultipleInserts(): void - { - $builder = $this->getSQLiteBuilder(); - $builder->getConnection()->shouldReceive('insert')->once()->with('insert into "users" ("email", "name") select ? as "email", ? as "name" union select ? as "email", ? as "name"', ['foo', 'taylor', 'bar', 'dayle'] - )->andReturn(true); - $result = $builder->from('users')->insert( - [['email' => 'foo', 'name' => 'taylor'], ['email' => 'bar', 'name' => 'dayle']] - ); - $this->assertTrue($result); - } - - - public function testInsertGetIdMethod(): void - { - $builder = $this->getBuilder(); - $builder->getProcessor()->shouldReceive('processInsertGetId')->once()->with($builder, 'insert into "users" ("email") values (?)', ['foo'], 'id')->andReturn(1); - $result = $builder->from('users')->insertGetId(['email' => 'foo'], 'id'); - $this->assertEquals(1, $result); - } - - - public function testInsertGetIdMethodRemovesExpressions(): void - { - $builder = $this->getBuilder(); - $builder->getProcessor()->shouldReceive('processInsertGetId')->once()->with($builder, 'insert into "users" ("email", "bar") values (?, bar)', ['foo'], 'id')->andReturn(1); - $result = $builder->from('users')->insertGetId( - ['email' => 'foo', 'bar' => new Illuminate\Database\Query\Expression('bar')], 'id'); - $this->assertEquals(1, $result); - } - - - public function testInsertMethodRespectsRawBindings(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('insert')->once()->with('insert into "users" ("email") values (CURRENT TIMESTAMP)', [] - )->andReturn(true); - $result = $builder->from('users')->insert(['email' => new Raw('CURRENT TIMESTAMP')]); - $this->assertTrue($result); - } - - - public function testUpdateMethod(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('update')->once()->with('update "users" set "email" = ?, "name" = ? where "id" = ?', ['foo', 'bar', 1] - )->andReturn(1); - $result = $builder->from('users')->where('id', '=', 1)->update(['email' => 'foo', 'name' => 'bar']); - $this->assertEquals(1, $result); - - $builder = $this->getMySqlBuilder(); - $builder->getConnection()->shouldReceive('update')->once()->with('update `users` set `email` = ?, `name` = ? where `id` = ? order by `foo` desc limit 5', ['foo', 'bar', 1] - )->andReturn(1); - $result = $builder->from('users')->where('id', '=', 1)->orderBy('foo', 'desc')->limit(5)->update( - ['email' => 'foo', 'name' => 'bar'] - ); - $this->assertEquals(1, $result); - } - - - public function testUpdateMethodWithJoins(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('update')->once()->with('update "users" inner join "orders" on "users"."id" = "orders"."user_id" set "email" = ?, "name" = ? where "users"."id" = ?', ['foo', 'bar', 1] - )->andReturn(1); - $result = $builder->from('users')->join('orders', 'users.id', '=', 'orders.user_id')->where('users.id', '=', 1)->update( - ['email' => 'foo', 'name' => 'bar'] - ); - $this->assertEquals(1, $result); - } - - - public function testUpdateMethodWithoutJoinsOnPostgres(): void - { - $builder = $this->getPostgresBuilder(); - $builder->getConnection()->shouldReceive('update')->once()->with('update "users" set "email" = ?, "name" = ? where "id" = ?', ['foo', 'bar', 1] - )->andReturn(1); - $result = $builder->from('users')->where('id', '=', 1)->update(['email' => 'foo', 'name' => 'bar']); - $this->assertEquals(1, $result); - } - - - public function testUpdateMethodWithJoinsOnPostgres(): void - { - $builder = $this->getPostgresBuilder(); - $builder->getConnection()->shouldReceive('update')->once()->with('update "users" set "email" = ?, "name" = ? from "orders" where "users"."id" = ? and "users"."id" = "orders"."user_id"', ['foo', 'bar', 1] - )->andReturn(1); - $result = $builder->from('users')->join('orders', 'users.id', '=', 'orders.user_id')->where('users.id', '=', 1)->update( - ['email' => 'foo', 'name' => 'bar'] - ); - $this->assertEquals(1, $result); - } - - - public function testUpdateMethodRespectsRaw(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('update')->once()->with('update "users" set "email" = foo, "name" = ? where "id" = ?', ['bar', 1] - )->andReturn(1); - $result = $builder->from('users')->where('id', '=', 1)->update(['email' => new Raw('foo'), 'name' => 'bar']); - $this->assertEquals(1, $result); - } - - - public function testDeleteMethod(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('delete')->once()->with('delete from "users" where "email" = ?', ['foo'] - )->andReturn(1); - $result = $builder->from('users')->where('email', '=', 'foo')->delete(); - $this->assertEquals(1, $result); - - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('delete')->once()->with('delete from "users" where "id" = ?', [1])->andReturn(1); - $result = $builder->from('users')->delete(1); - $this->assertEquals(1, $result); - } - - - public function testDeleteWithJoinMethod(): void - { - $builder = $this->getMySqlBuilder(); - $builder->getConnection()->shouldReceive('delete')->once()->with('delete `users` from `users` inner join `contacts` on `users`.`id` = `contacts`.`id` where `email` = ?', ['foo'] - )->andReturn(1); - $result = $builder->from('users')->join('contacts', 'users.id', '=', 'contacts.id')->where('email', '=', 'foo')->delete(); - $this->assertEquals(1, $result); - - $builder = $this->getMySqlBuilder(); - $builder->getConnection()->shouldReceive('delete')->once()->with('delete `users` from `users` inner join `contacts` on `users`.`id` = `contacts`.`id` where `id` = ?', [1] - )->andReturn(1); - $result = $builder->from('users')->join('contacts', 'users.id', '=', 'contacts.id')->delete(1); - $this->assertEquals(1, $result); - } - - - public function testTruncateMethod(): void - { - $builder = $this->getBuilder(); - $builder->getConnection()->shouldReceive('statement')->once()->with('truncate "users"', []); - $builder->from('users')->truncate(); - - $sqlite = new Illuminate\Database\Query\Grammars\SQLiteGrammar; - $builder = $this->getBuilder(); - $builder->from('users'); - $this->assertEquals([ - 'delete from sqlite_sequence where name = ?' => ['users'], - 'delete from "users"' => [], - ], $sqlite->compileTruncate($builder)); - } - - - public function testPostgresInsertGetId(): void - { - $builder = $this->getPostgresBuilder(); - $builder->getProcessor()->shouldReceive('processInsertGetId')->once()->with($builder, 'insert into "users" ("email") values (?) returning "id"', ['foo'], 'id')->andReturn(1); - $result = $builder->from('users')->insertGetId(['email' => 'foo'], 'id'); - $this->assertEquals(1, $result); - } - - - public function testMySqlWrapping(): void - { - $builder = $this->getMySqlBuilder(); - $builder->select('*')->from('users'); - $this->assertEquals('select * from `users`', $builder->toSql()); - } - - - public function testSQLiteOrderBy(): void - { - $builder = $this->getSQLiteBuilder(); - $builder->select('*')->from('users')->orderBy('email', 'desc'); - $this->assertEquals('select * from "users" order by "email" desc', $builder->toSql()); - } - - - public function testSqlServerLimitsAndOffsets(): void - { - $builder = $this->getSqlServerBuilder(); - $builder->select('*')->from('users')->take(10); - $this->assertEquals('select top 10 * from [users]', $builder->toSql()); - - $builder = $this->getSqlServerBuilder(); - $builder->select('*')->from('users')->skip(10); - $this->assertEquals('select * from (select *, row_number() over (order by (select 0)) as row_num from [users]) as temp_table where row_num >= 11', $builder->toSql()); - - $builder = $this->getSqlServerBuilder(); - $builder->select('*')->from('users')->skip(10)->take(10); - $this->assertEquals('select * from (select *, row_number() over (order by (select 0)) as row_num from [users]) as temp_table where row_num between 11 and 20', $builder->toSql()); - - $builder = $this->getSqlServerBuilder(); - $builder->select('*')->from('users')->skip(10)->take(10)->orderBy('email', 'desc'); - $this->assertEquals('select * from (select *, row_number() over (order by [email] desc) as row_num from [users]) as temp_table where row_num between 11 and 20', $builder->toSql()); - } - - - public function testMergeWheresCanMergeWheresAndBindings(): void - { - $builder = $this->getBuilder(); - $builder->wheres = ['foo']; - $builder->mergeWheres(['wheres'], [12 => 'foo', 13 => 'bar']); - $this->assertEquals(['foo', 'wheres'], $builder->wheres); - $this->assertEquals(['foo', 'bar'], $builder->getBindings()); - } - - - public function testProvidingNullOrFalseAsSecondParameterBuildsCorrectly(): void - { - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->where('foo', null); - $this->assertEquals('select * from "users" where "foo" is null', $builder->toSql()); - } - - - public function testDynamicWhere(): void - { - $method = 'whereFooBarAndBazOrQux'; - $parameters = ['corge', 'waldo', 'fred']; - $builder = m::mock(Builder::class)->makePartial(); - - $builder->shouldReceive('where')->with('foo_bar', '=', $parameters[0], 'and')->once()->andReturn($builder); - $builder->shouldReceive('where')->with('baz', '=', $parameters[1], 'and')->once()->andReturn($builder); - $builder->shouldReceive('where')->with('qux', '=', $parameters[2], 'or')->once()->andReturn($builder); - - $this->assertEquals($builder, $builder->dynamicWhere($method, $parameters)); - } - - - public function testDynamicWhereIsNotGreedy(): void - { - $method = 'whereIosVersionAndAndroidVersionOrOrientation'; - $parameters = ['6.1', '4.2', 'Vertical']; - $builder = m::mock(Builder::class)->makePartial(); - - $builder->shouldReceive('where')->with('ios_version', '=', '6.1', 'and')->once()->andReturn($builder); - $builder->shouldReceive('where')->with('android_version', '=', '4.2', 'and')->once()->andReturn($builder); - $builder->shouldReceive('where')->with('orientation', '=', 'Vertical', 'or')->once()->andReturn($builder); - - $builder->dynamicWhere($method, $parameters); - } - - - public function testCallTriggersDynamicWhere(): void - { - $builder = $this->getBuilder(); - - $this->assertEquals($builder, $builder->whereFooAndBar('baz', 'qux')); - $this->assertCount(2, $builder->wheres); - } - - - public function testBuilderThrowsExpectedExceptionWithUndefinedMethod(): void - { - $this->expectException(BadMethodCallException::class); - $builder = $this->getBuilder(); - - $builder->noValidMethodHere(); - } - - - public function setupCacheTestQuery($cache, $driver): Builder - { - $connection = m::mock(ConnectionInterface::class); - $connection->shouldReceive('getName')->andReturn('connection_name'); - $connection->shouldReceive('getCacheManager')->once()->andReturn($cache); - $cache->shouldReceive('driver')->once()->andReturn($driver); - $grammar = new Illuminate\Database\Query\Grammars\Grammar; - $processor = m::mock(Processor::class); - - $builder = $this->getMock(Builder::class, ['getFresh'], [$connection, $grammar, $processor]); - $builder->expects($this->once())->method('getFresh')->with($this->equalTo(['*']))->willReturn( - ['results'] - ); - return $builder->select('*')->from('users')->where('email', 'foo@bar.com'); - } - - - public function testMySqlLock(): void - { - $builder = $this->getMySqlBuilder(); - $builder->select('*')->from('foo')->where('bar', '=', 'baz')->lock(); - $this->assertEquals('select * from `foo` where `bar` = ? for update', $builder->toSql()); - $this->assertEquals(['baz'], $builder->getBindings()); - - $builder = $this->getMySqlBuilder(); - $builder->select('*')->from('foo')->where('bar', '=', 'baz')->lock(false); - $this->assertEquals('select * from `foo` where `bar` = ? lock in share mode', $builder->toSql()); - $this->assertEquals(['baz'], $builder->getBindings()); - } - - - public function testPostgresLock(): void - { - $builder = $this->getPostgresBuilder(); - $builder->select('*')->from('foo')->where('bar', '=', 'baz')->lock(); - $this->assertEquals('select * from "foo" where "bar" = ? for update', $builder->toSql()); - $this->assertEquals(['baz'], $builder->getBindings()); - - $builder = $this->getPostgresBuilder(); - $builder->select('*')->from('foo')->where('bar', '=', 'baz')->lock(false); - $this->assertEquals('select * from "foo" where "bar" = ? for share', $builder->toSql()); - $this->assertEquals(['baz'], $builder->getBindings()); - } - - - public function testSqlServerLock(): void - { - $builder = $this->getSqlServerBuilder(); - $builder->select('*')->from('foo')->where('bar', '=', 'baz')->lock(); - $this->assertEquals('select * from [foo] with(rowlock,updlock,holdlock) where [bar] = ?', $builder->toSql()); - $this->assertEquals(['baz'], $builder->getBindings()); - - $builder = $this->getSqlServerBuilder(); - $builder->select('*')->from('foo')->where('bar', '=', 'baz')->lock(false); - $this->assertEquals('select * from [foo] with(rowlock,holdlock) where [bar] = ?', $builder->toSql()); - $this->assertEquals(['baz'], $builder->getBindings()); - } - - - public function testBindingOrder(): void - { - $expectedSql = 'select * from "users" inner join "othertable" on "bar" = ? where "registered" = ? group by "city" having "population" > ? order by match ("foo") against(?)'; - $expectedBindings = ['foo', 1, 3, 'bar']; - - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->join('othertable', function($join) { $join->where('bar', '=', 'foo'); })->where('registered', 1)->groupBy('city')->having('population', '>', 3)->orderByRaw('match ("foo") against(?)', ['bar'] - ); - $this->assertEquals($expectedSql, $builder->toSql()); - $this->assertEquals($expectedBindings, $builder->getBindings()); - - // order of statements reversed - $builder = $this->getBuilder(); - $builder->select('*')->from('users')->orderByRaw('match ("foo") against(?)', ['bar'])->having('population', '>', 3)->groupBy('city')->where('registered', 1)->join('othertable', function($join) { $join->where('bar', '=', 'foo'); }); - $this->assertEquals($expectedSql, $builder->toSql()); - $this->assertEquals($expectedBindings, $builder->getBindings()); - } - - - public function testAddBindingWithArrayMergesBindings(): void - { - $builder = $this->getBuilder(); - $builder->addBinding(['foo', 'bar']); - $builder->addBinding(['baz']); - $this->assertEquals(['foo', 'bar', 'baz'], $builder->getBindings()); - } - - - public function testAddBindingWithArrayMergesBindingsInCorrectOrder(): void - { - $builder = $this->getBuilder(); - $builder->addBinding(['bar', 'baz'], 'having'); - $builder->addBinding(['foo'], 'where'); - $this->assertEquals(['foo', 'bar', 'baz'], $builder->getBindings()); - } - - - public function testMergeBuilders(): void - { - $builder = $this->getBuilder(); - $builder->addBinding(['foo', 'bar']); - $otherBuilder = $this->getBuilder(); - $otherBuilder->addBinding(['baz']); - $builder->mergeBindings($otherBuilder); - $this->assertEquals(['foo', 'bar', 'baz'], $builder->getBindings()); - } - - - public function testMergeBuildersBindingOrder(): void - { - $builder = $this->getBuilder(); - $builder->addBinding('foo', 'where'); - $builder->addBinding('baz', 'having'); - $otherBuilder = $this->getBuilder(); - $otherBuilder->addBinding('bar', 'where'); - $builder->mergeBindings($otherBuilder); - $this->assertEquals(['foo', 'bar', 'baz'], $builder->getBindings()); - } - - public function testChunkByIdOnArrays(): void - { - $builder = $this->getMockQueryBuilder(); - $builder->orders[] = ['column' => 'foobar', 'direction' => 'asc']; - - $chunk1 = [['someIdField' => 1], ['someIdField' => 2]]; - $chunk2 = [['someIdField' => 10], ['someIdField' => 11]]; - $chunk3 = []; - $builder->shouldReceive('forPageAfterId')->once()->with(2, 0, 'someIdField')->andReturnSelf(); - $builder->shouldReceive('forPageAfterId')->once()->with(2, 2, 'someIdField')->andReturnSelf(); - $builder->shouldReceive('forPageAfterId')->once()->with(2, 11, 'someIdField')->andReturnSelf(); - $builder->shouldReceive('get')->times(3)->andReturn($chunk1, $chunk2, $chunk3); - - $callbackAssertor = m::mock(stdClass::class); - $callbackAssertor->shouldReceive('doSomething')->once()->with($chunk1); - $callbackAssertor->shouldReceive('doSomething')->once()->with($chunk2); - $callbackAssertor->shouldReceive('doSomething')->never()->with($chunk3); - - $builder->chunkById(2, function ($results) use ($callbackAssertor) { - $callbackAssertor->doSomething($results); - }, 'someIdField'); - } - - public function testChunkPaginatesUsingIdWithLastChunkComplete(): void - { - $builder = $this->getMockQueryBuilder(); - $builder->orders[] = ['column' => 'foobar', 'direction' => 'asc']; - - $chunk1 = [(object) ['someIdField' => 1], (object) ['someIdField' => 2]]; - $chunk2 = [(object) ['someIdField' => 10], (object) ['someIdField' => 11]]; - $chunk3 = []; - $builder->shouldReceive('forPageAfterId')->once()->with(2, 0, 'someIdField')->andReturnSelf(); - $builder->shouldReceive('forPageAfterId')->once()->with(2, 2, 'someIdField')->andReturnSelf(); - $builder->shouldReceive('forPageAfterId')->once()->with(2, 11, 'someIdField')->andReturnSelf(); - $builder->shouldReceive('get')->times(3)->andReturn($chunk1, $chunk2, $chunk3); - - $callbackAssertor = m::mock(stdClass::class); - $callbackAssertor->shouldReceive('doSomething')->once()->with($chunk1); - $callbackAssertor->shouldReceive('doSomething')->once()->with($chunk2); - $callbackAssertor->shouldReceive('doSomething')->never()->with($chunk3); - - $builder->chunkById(2, function ($results) use ($callbackAssertor) { - $callbackAssertor->doSomething($results); - }, 'someIdField'); - } - - public function testChunkPaginatesUsingIdWithLastChunkPartial(): void - { - $builder = $this->getMockQueryBuilder(); - $builder->orders[] = ['column' => 'foobar', 'direction' => 'asc']; - - $chunk1 = [(object) ['someIdField' => 1], (object) ['someIdField' => 2]]; - $chunk2 = [(object) ['someIdField' => 10]]; - $builder->shouldReceive('forPageAfterId')->once()->with(2, 0, 'someIdField')->andReturnSelf(); - $builder->shouldReceive('forPageAfterId')->once()->with(2, 2, 'someIdField')->andReturnSelf(); - $builder->shouldReceive('get')->times(2)->andReturn($chunk1, $chunk2); - - $callbackAssertor = m::mock(stdClass::class); - $callbackAssertor->shouldReceive('doSomething')->once()->with($chunk1); - $callbackAssertor->shouldReceive('doSomething')->once()->with($chunk2); - - $builder->chunkById(2, function ($results) use ($callbackAssertor) { - $callbackAssertor->doSomething($results); - }, 'someIdField'); - } - - public function testChunkPaginatesUsingIdWithCountZero(): void - { - $builder = $this->getMockQueryBuilder(); - $builder->orders[] = ['column' => 'foobar', 'direction' => 'asc']; - - $chunk = []; - $builder->shouldReceive('forPageAfterId')->once()->with(0, 0, 'someIdField')->andReturnSelf(); - $builder->shouldReceive('get')->times(1)->andReturn($chunk); - - $callbackAssertor = m::mock(stdClass::class); - $callbackAssertor->shouldReceive('doSomething')->never(); - - $builder->chunkById(0, function ($results) use ($callbackAssertor) { - $callbackAssertor->doSomething($results); - }, 'someIdField'); - } - - public function testChunkPaginatesUsingIdWithAlias(): void - { - $builder = $this->getMockQueryBuilder(); - $builder->orders[] = ['column' => 'foobar', 'direction' => 'asc']; - - $chunk1 = [(object) ['table_id' => 1], (object) ['table_id' => 10]]; - $chunk2 = []; - $builder->shouldReceive('forPageAfterId')->once()->with(2, 0, 'table.id')->andReturnSelf(); - $builder->shouldReceive('forPageAfterId')->once()->with(2, 10, 'table.id')->andReturnSelf(); - $builder->shouldReceive('get')->times(2)->andReturn($chunk1, $chunk2); - - $callbackAssertor = m::mock(stdClass::class); - $callbackAssertor->shouldReceive('doSomething')->once()->with($chunk1); - $callbackAssertor->shouldReceive('doSomething')->never()->with($chunk2); - - $builder->chunkById(2, function ($results) use ($callbackAssertor) { - $callbackAssertor->doSomething($results); - }, 'table.id', 'table_id'); - } - - protected function getBuilder(): Builder - { - $grammar = new Illuminate\Database\Query\Grammars\Grammar; - $processor = m::mock(Processor::class); - return new Builder(m::mock(ConnectionInterface::class), $grammar, $processor); - } - - - protected function getPostgresBuilder(): Builder - { - $grammar = new Illuminate\Database\Query\Grammars\PostgresGrammar; - $processor = m::mock(Processor::class); - return new Builder(m::mock(ConnectionInterface::class), $grammar, $processor); - } - - - protected function getMySqlBuilder(): Builder - { - $grammar = new Illuminate\Database\Query\Grammars\MySqlGrammar; - $processor = m::mock(Processor::class); - return new Builder(m::mock(ConnectionInterface::class), $grammar, $processor); - } - - - protected function getSQLiteBuilder(): Builder - { - $grammar = new Illuminate\Database\Query\Grammars\SQLiteGrammar; - $processor = m::mock(Processor::class); - return new Builder(m::mock(ConnectionInterface::class), $grammar, $processor); - } - - - protected function getSqlServerBuilder(): Builder - { - $grammar = new Illuminate\Database\Query\Grammars\SqlServerGrammar; - $processor = m::mock(Processor::class); - return new Builder(m::mock(ConnectionInterface::class), $grammar, $processor); - } - - - protected function getMySqlBuilderWithProcessor(): Builder - { - $grammar = new Illuminate\Database\Query\Grammars\MySqlGrammar; - $processor = new Illuminate\Database\Query\Processors\MySqlProcessor; - return new Builder(m::mock(ConnectionInterface::class), $grammar, $processor); - } - - /** - * @return MockInterface|\Illuminate\Database\Query\Builder - */ - protected function getMockQueryBuilder(): MockInterface|Builder - { - return m::mock(Builder::class, [ - m::mock(ConnectionInterface::class), - new Grammar, - m::mock(Processor::class), - ])->makePartial()->shouldAllowMockingProtectedMethods(); - } - -} diff --git a/tests/Database/DatabaseSQLiteProcessorTest.php b/tests/Database/DatabaseSQLiteProcessorTest.php deleted file mode 100644 index 183ac200..00000000 --- a/tests/Database/DatabaseSQLiteProcessorTest.php +++ /dev/null @@ -1,25 +0,0 @@ - 'id'], ['name' => 'name'], ['name' => 'email']]; - $expected = ['id', 'name', 'email']; - - $this->assertEquals($expected, $processor->processColumnListing($listing)); - - // convert listing to objects to simulate PDO::FETCH_CLASS - foreach($listing as &$row) - { - $row = (object) $row; - } - - $this->assertEquals($expected, $processor->processColumnListing($listing)); - } - -} diff --git a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php b/tests/Database/DatabaseSQLiteSchemaGrammarTest.php deleted file mode 100755 index bd346fb8..00000000 --- a/tests/Database/DatabaseSQLiteSchemaGrammarTest.php +++ /dev/null @@ -1,419 +0,0 @@ -create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create table "users" ("id" integer not null primary key autoincrement, "email" varchar not null)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $expected = [ - 'alter table "users" add column "id" integer not null primary key autoincrement', - 'alter table "users" add column "email" varchar not null', - ]; - $this->assertEquals($expected, $statements); - } - - - public function testDropTable() - { - $blueprint = new Blueprint('users'); - $blueprint->drop(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop table "users"', $statements[0]); - } - - - public function testDropTableIfExists() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIfExists(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop table if exists "users"', $statements[0]); - } - - - public function testDropUnique() - { - $blueprint = new Blueprint('users'); - $blueprint->dropUnique('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop index foo', $statements[0]); - } - - - public function testDropIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIndex('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop index foo', $statements[0]); - } - - - public function testRenameTable() - { - $blueprint = new Blueprint('users'); - $blueprint->rename('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" rename to "foo"', $statements[0]); - } - - - public function testAddingPrimaryKey() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('foo')->primary(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create table "users" ("foo" varchar not null, primary key ("foo"))', $statements[0]); - } - - - public function testAddingForeignKey() - { - $blueprint = new Blueprint('users'); - $blueprint->create(); - $blueprint->string('foo')->primary(); - $blueprint->string('order_id'); - $blueprint->foreign('order_id')->references('id')->on('orders'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create table "users" ("foo" varchar not null, "order_id" varchar not null, foreign key("order_id") references "orders"("id"), primary key ("foo"))', $statements[0]); - } - - - public function testAddingUniqueKey() - { - $blueprint = new Blueprint('users'); - $blueprint->unique('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create unique index bar on "users" ("foo")', $statements[0]); - } - - - public function testAddingIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create index baz on "users" ("foo", "bar")', $statements[0]); - } - - - public function testAddingIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "id" integer not null primary key autoincrement', $statements[0]); - } - - - public function testAddingBigIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->bigIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "id" integer not null primary key autoincrement', $statements[0]); - } - - - public function testAddingString() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" varchar not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" varchar not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default('bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" varchar null default \'bar\'', $statements[0]); - } - - - public function testAddingText() - { - $blueprint = new Blueprint('users'); - $blueprint->text('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" text not null', $statements[0]); - } - - - public function testAddingBigInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" integer not null primary key autoincrement', $statements[0]); - } - - - public function testAddingInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" integer not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" integer not null primary key autoincrement', $statements[0]); - } - - - public function testAddingMediumInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" integer not null', $statements[0]); - } - - - public function testAddingTinyInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" integer not null', $statements[0]); - } - - - public function testAddingSmallInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" integer not null', $statements[0]); - } - - - public function testAddingFloat() - { - $blueprint = new Blueprint('users'); - $blueprint->float('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" float not null', $statements[0]); - } - - - public function testAddingDouble() - { - $blueprint = new Blueprint('users'); - $blueprint->double('foo', 15, 8); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" float not null', $statements[0]); - } - - - public function testAddingDecimal() - { - $blueprint = new Blueprint('users'); - $blueprint->decimal('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" float not null', $statements[0]); - } - - - public function testAddingBoolean() - { - $blueprint = new Blueprint('users'); - $blueprint->boolean('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" tinyint not null', $statements[0]); - } - - - public function testAddingEnum() - { - $blueprint = new Blueprint('users'); - $blueprint->enum('foo', ['bar', 'baz']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" varchar not null', $statements[0]); - } - - - public function testAddingDate() - { - $blueprint = new Blueprint('users'); - $blueprint->date('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" date not null', $statements[0]); - } - - - public function testAddingDateTime() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" datetime not null', $statements[0]); - } - - - public function testAddingTime() - { - $blueprint = new Blueprint('users'); - $blueprint->time('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" time not null', $statements[0]); - } - - - public function testAddingTimeStamp() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" datetime not null', $statements[0]); - } - - - public function testAddingTimeStamps() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(2, $statements); - $expected = [ - 'alter table "users" add column "created_at" datetime not null', - 'alter table "users" add column "updated_at" datetime not null', - ]; - $this->assertEquals($expected, $statements); - } - - - public function testAddingRememberToken() - { - $blueprint = new Blueprint('users'); - $blueprint->rememberToken(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "remember_token" varchar null', $statements[0]); - } - - - public function testAddingBinary() - { - $blueprint = new Blueprint('users'); - $blueprint->binary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add column "foo" blob not null', $statements[0]); - } - - - protected function getConnection() - { - return m::mock(Connection::class); - } - - - public function getGrammar() - { - return new Illuminate\Database\Schema\Grammars\SQLiteGrammar; - } - -} diff --git a/tests/Database/DatabaseSchemaBlueprintTest.php b/tests/Database/DatabaseSchemaBlueprintTest.php deleted file mode 100755 index 93e741a9..00000000 --- a/tests/Database/DatabaseSchemaBlueprintTest.php +++ /dev/null @@ -1,61 +0,0 @@ -shouldReceive('statement')->once()->with('foo'); - $conn->shouldReceive('statement')->once()->with('bar'); - $grammar = m::mock(MySqlGrammar::class); - $blueprint = $this->getMock(Blueprint::class, ['toSql'], ['users']); - $blueprint->expects($this->once())->method('toSql')->with($this->equalTo($conn), $this->equalTo($grammar))->willReturn( - ['foo', 'bar'] - ); - - $blueprint->build($conn, $grammar); - } - - - public function testIndexDefaultNames() - { - $blueprint = new Blueprint('users'); - $blueprint->unique(['foo', 'bar']); - $commands = $blueprint->getCommands(); - $this->assertEquals('users_foo_bar_unique', $commands[0]->index); - - $blueprint = new Blueprint('users'); - $blueprint->index('foo'); - $commands = $blueprint->getCommands(); - $this->assertEquals('users_foo_index', $commands[0]->index); - } - - - public function testDropIndexDefaultNames() - { - $blueprint = new Blueprint('users'); - $blueprint->dropUnique(['foo', 'bar']); - $commands = $blueprint->getCommands(); - $this->assertEquals('users_foo_bar_unique', $commands[0]->index); - - $blueprint = new Blueprint('users'); - $blueprint->dropIndex(['foo']); - $commands = $blueprint->getCommands(); - $this->assertEquals('users_foo_index', $commands[0]->index); - } - - -} diff --git a/tests/Database/DatabaseSchemaBuilderTest.php b/tests/Database/DatabaseSchemaBuilderTest.php deleted file mode 100755 index 6d86feab..00000000 --- a/tests/Database/DatabaseSchemaBuilderTest.php +++ /dev/null @@ -1,30 +0,0 @@ -shouldReceive('getSchemaGrammar')->andReturn($grammar); - $builder = new Builder($connection); - $grammar->shouldReceive('compileTableExists')->once()->andReturn('sql'); - $connection->shouldReceive('getTablePrefix')->once()->andReturn('prefix_'); - $connection->shouldReceive('select')->once()->with('sql', ['prefix_table'])->andReturn(['prefix_table']); - - $this->assertTrue($builder->hasTable('table')); - } - -} diff --git a/tests/Database/DatabaseSeederTest.php b/tests/Database/DatabaseSeederTest.php deleted file mode 100755 index e50ccff8..00000000 --- a/tests/Database/DatabaseSeederTest.php +++ /dev/null @@ -1,52 +0,0 @@ -setContainer($container = m::mock(Container::class)); - $output = m::mock(OutputInterface::class); - $output->shouldReceive('writeln')->once()->andReturn('foo'); - $command = m::mock(Command::class); - $command->shouldReceive('getOutput')->once()->andReturn($output); - $seeder->setCommand($command); - $container->shouldReceive('make')->once()->with('ClassName')->andReturn($child = m::mock('StdClass')); - $child->shouldReceive('setContainer')->once()->with($container)->andReturn($child); - $child->shouldReceive('setCommand')->once()->with($command)->andReturn($child); - $child->shouldReceive('run')->once(); - - $seeder->call('ClassName'); - } - - - public function testSetContainer() - { - $seeder = new Seeder; - $container = m::mock(Container::class); - $this->assertEquals($seeder->setContainer($container), $seeder); - } - - - public function testSetCommand() - { - $seeder = new Seeder; - $command = m::mock(Command::class); - $this->assertEquals($seeder->setCommand($command), $seeder); - } - -} diff --git a/tests/Database/DatabaseSoftDeletingScopeTest.php b/tests/Database/DatabaseSoftDeletingScopeTest.php deleted file mode 100644 index 144cef10..00000000 --- a/tests/Database/DatabaseSoftDeletingScopeTest.php +++ /dev/null @@ -1,122 +0,0 @@ -shouldReceive('getModel')->once()->andReturn($model = m::mock('StdClass')); - $model->shouldReceive('getQualifiedDeletedAtColumn')->once()->andReturn('table.deleted_at'); - $builder->shouldReceive('whereNull')->once()->with('table.deleted_at'); - $scope->shouldReceive('extend')->once(); - - $scope->apply($builder); - } - - - public function testScopeCanRemoveDeletedAtConstraints() - { - $scope = new Illuminate\Database\Eloquent\SoftDeletingScope; - $builder = m::mock(Builder::class); - $builder->shouldReceive('getModel')->andReturn($model = m::mock('StdClass')); - $model->shouldReceive('getQualifiedDeletedAtColumn')->andReturn('table.deleted_at'); - $builder->shouldReceive('getQuery')->andReturn($query = m::mock('StdClass')); - $query->wheres = [['type' => 'Null', 'column' => 'foo'], ['type' => 'Null', 'column' => 'table.deleted_at']]; - $scope->remove($builder); - - $this->assertEquals($query->wheres, [['type' => 'Null', 'column' => 'foo']]); - } - - - public function testForceDeleteExtension() - { - $builder = m::mock(Builder::class); - $builder->makePartial(); - $scope = new Illuminate\Database\Eloquent\SoftDeletingScope; - $scope->extend($builder); - $callback = $builder->getMacro('forceDelete'); - $givenBuilder = m::mock(Builder::class); - $givenBuilder->shouldReceive('getQuery')->andReturn($query = m::mock('StdClass')); - $query->shouldReceive('delete')->once(); - - $callback($givenBuilder); - } - - - public function testRestoreExtension() - { - $builder = m::mock(Builder::class); - $builder->makePartial(); - $scope = new Illuminate\Database\Eloquent\SoftDeletingScope; - $scope->extend($builder); - $callback = $builder->getMacro('restore'); - $givenBuilder = m::mock(Builder::class); - $givenBuilder->shouldReceive('withTrashed')->once(); - $givenBuilder->shouldReceive('getModel')->once()->andReturn($model = m::mock('StdClass')); - $model->shouldReceive('getDeletedAtColumn')->once()->andReturn('deleted_at'); - $givenBuilder->shouldReceive('update')->once()->with(['deleted_at' => null]); - - $callback($givenBuilder); - } - - - public function testWithTrashedExtension() - { - $builder = m::mock(Builder::class); - $builder->makePartial(); - $scope = m::mock('Illuminate\Database\Eloquent\SoftDeletingScope[remove]'); - $scope->extend($builder); - $callback = $builder->getMacro('withTrashed'); - $givenBuilder = m::mock(Builder::class); - $scope->shouldReceive('remove')->once()->with($givenBuilder); - $result = $callback($givenBuilder); - - $this->assertEquals($givenBuilder, $result); - } - - - public function testOnlyTrashedExtension() - { - $builder = m::mock(Builder::class); - $builder->makePartial(); - $scope = m::mock('Illuminate\Database\Eloquent\SoftDeletingScope[remove]'); - $scope->extend($builder); - $callback = $builder->getMacro('onlyTrashed'); - $givenBuilder = m::mock(Builder::class); - $scope->shouldReceive('remove')->once()->with($givenBuilder); - $givenBuilder->shouldReceive('getQuery')->andReturn($query = m::mock('StdClass')); - $givenBuilder->shouldReceive('getModel')->andReturn($model = m::mock('StdClass')); - $model->shouldReceive('getQualifiedDeletedAtColumn')->andReturn('table.deleted_at'); - $query->shouldReceive('whereNotNull')->once()->with('table.deleted_at'); - $result = $callback($givenBuilder); - - $this->assertEquals($givenBuilder, $result); - } - -} - - -class DatabaseSoftDeletingScopeBuilderStub { - public $extensions = []; - public $onDelete; - public function extend($name, $callback) - { - $this->extensions[$name] = $callback; - } - public function onDelete($callback) - { - $this->onDelete = $callback; - } -} diff --git a/tests/Database/DatabaseSoftDeletingTraitTest.php b/tests/Database/DatabaseSoftDeletingTraitTest.php deleted file mode 100644 index f3ce3e49..00000000 --- a/tests/Database/DatabaseSoftDeletingTraitTest.php +++ /dev/null @@ -1,91 +0,0 @@ -makePartial(); - $model->shouldReceive('newQuery')->andReturn($query = m::mock('StdClass')); - $query->shouldReceive('where')->once()->with('id', 1)->andReturn($query); - $query->shouldReceive('update')->once()->with(['deleted_at' => 'date-time']); - $model->delete(); - - $this->assertInstanceOf(Carbon::class, $model->deleted_at); - } - - - public function testRestore() - { - $model = m::mock('DatabaseSoftDeletingTraitStub'); - $model->makePartial(); - $model->shouldReceive('fireModelEvent')->with('restoring')->andReturn(true); - $model->shouldReceive('save')->once(); - $model->shouldReceive('fireModelEvent')->with('restored', false)->andReturn(true); - - $model->restore(); - - $this->assertNull($model->deleted_at); - } - - - public function testRestoreCancel() - { - $model = m::mock('DatabaseSoftDeletingTraitStub'); - $model->makePartial(); - $model->shouldReceive('fireModelEvent')->with('restoring')->andReturn(false); - $model->shouldReceive('save')->never(); - - $this->assertFalse($model->restore()); - } - -} - - -class DatabaseSoftDeletingTraitStub { - use Illuminate\Database\Eloquent\SoftDeletes; - public $deleted_at; - public function newQuery() - { - // - } - public function getKey() - { - return 1; - } - public function getKeyName() - { - return 'id'; - } - public function save() - { - // - } - public function delete() - { - return $this->performDeleteOnModel(); - } - public function fireModelEvent() - { - // - } - public function freshTimestamp() - { - return Carbon::now(); - } - public function fromDateTime() - { - return 'date-time'; - } -} diff --git a/tests/Database/DatabaseSqlServerSchemaGrammarTest.php b/tests/Database/DatabaseSqlServerSchemaGrammarTest.php deleted file mode 100755 index f6e01fa9..00000000 --- a/tests/Database/DatabaseSqlServerSchemaGrammarTest.php +++ /dev/null @@ -1,443 +0,0 @@ -create(); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create table "users" ("id" int identity primary key not null, "email" nvarchar(255) not null)', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $blueprint->string('email'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "id" int identity primary key not null, "email" nvarchar(255) not null', $statements[0]); - } - - - public function testDropTable() - { - $blueprint = new Blueprint('users'); - $blueprint->drop(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop table "users"', $statements[0]); - } - - - public function testDropColumn() - { - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop column "foo"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn(['foo', 'bar']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop column "foo", "bar"', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->dropColumn('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop column "foo", "bar"', $statements[0]); - } - - - public function testDropPrimary() - { - $blueprint = new Blueprint('users'); - $blueprint->dropPrimary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop constraint foo', $statements[0]); - } - - - public function testDropUnique() - { - $blueprint = new Blueprint('users'); - $blueprint->dropUnique('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop index foo on "users"', $statements[0]); - } - - - public function testDropIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->dropIndex('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('drop index foo on "users"', $statements[0]); - } - - - public function testDropForeign() - { - $blueprint = new Blueprint('users'); - $blueprint->dropForeign('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop constraint foo', $statements[0]); - } - - - public function testDropTimestamps() - { - $blueprint = new Blueprint('users'); - $blueprint->dropTimestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" drop column "created_at", "updated_at"', $statements[0]); - } - - - public function testRenameTable() - { - $blueprint = new Blueprint('users'); - $blueprint->rename('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('sp_rename "users", "foo"', $statements[0]); - } - - - public function testAddingPrimaryKey() - { - $blueprint = new Blueprint('users'); - $blueprint->primary('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add constraint bar primary key ("foo")', $statements[0]); - } - - - public function testAddingUniqueKey() - { - $blueprint = new Blueprint('users'); - $blueprint->unique('foo', 'bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create unique index bar on "users" ("foo")', $statements[0]); - } - - - public function testAddingIndex() - { - $blueprint = new Blueprint('users'); - $blueprint->index(['foo', 'bar'], 'baz'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('create index baz on "users" ("foo", "bar")', $statements[0]); - } - - - public function testAddingIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->increments('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "id" int identity primary key not null', $statements[0]); - } - - - public function testAddingBigIncrementingID() - { - $blueprint = new Blueprint('users'); - $blueprint->bigIncrements('id'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "id" bigint identity primary key not null', $statements[0]); - } - - - public function testAddingString() - { - $blueprint = new Blueprint('users'); - $blueprint->string('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" nvarchar(255) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" nvarchar(100) not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->string('foo', 100)->nullable()->default('bar'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" nvarchar(100) null default \'bar\'', $statements[0]); - } - - - public function testAddingText() - { - $blueprint = new Blueprint('users'); - $blueprint->text('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" nvarchar(max) not null', $statements[0]); - } - - - public function testAddingBigInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" bigint not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->bigInteger('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" bigint identity primary key not null', $statements[0]); - } - - - public function testAddingInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->integer('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" int not null', $statements[0]); - - $blueprint = new Blueprint('users'); - $blueprint->integer('foo', true); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" int identity primary key not null', $statements[0]); - } - - - public function testAddingMediumInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->mediumInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" int not null', $statements[0]); - } - - - public function testAddingTinyInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->tinyInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" tinyint not null', $statements[0]); - } - - - public function testAddingSmallInteger() - { - $blueprint = new Blueprint('users'); - $blueprint->smallInteger('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" smallint not null', $statements[0]); - } - - - public function testAddingFloat() - { - $blueprint = new Blueprint('users'); - $blueprint->float('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" float not null', $statements[0]); - } - - - public function testAddingDouble() - { - $blueprint = new Blueprint('users'); - $blueprint->double('foo', 15, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" float not null', $statements[0]); - } - - - public function testAddingDecimal() - { - $blueprint = new Blueprint('users'); - $blueprint->decimal('foo', 5, 2); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" decimal(5, 2) not null', $statements[0]); - } - - - public function testAddingBoolean() - { - $blueprint = new Blueprint('users'); - $blueprint->boolean('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" bit not null', $statements[0]); - } - - - public function testAddingEnum() - { - $blueprint = new Blueprint('users'); - $blueprint->enum('foo', ['bar', 'baz']); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" nvarchar(255) not null', $statements[0]); - } - - - public function testAddingDate() - { - $blueprint = new Blueprint('users'); - $blueprint->date('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" date not null', $statements[0]); - } - - - public function testAddingDateTime() - { - $blueprint = new Blueprint('users'); - $blueprint->dateTime('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" datetime not null', $statements[0]); - } - - - public function testAddingTime() - { - $blueprint = new Blueprint('users'); - $blueprint->time('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" time not null', $statements[0]); - } - - - public function testAddingTimeStamp() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamp('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" datetime not null', $statements[0]); - } - - - public function testAddingTimeStamps() - { - $blueprint = new Blueprint('users'); - $blueprint->timestamps(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "created_at" datetime not null, "updated_at" datetime not null', $statements[0]); - } - - - public function testAddingRememberToken() - { - $blueprint = new Blueprint('users'); - $blueprint->rememberToken(); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "remember_token" nvarchar(100) null', $statements[0]); - } - - - public function testAddingBinary() - { - $blueprint = new Blueprint('users'); - $blueprint->binary('foo'); - $statements = $blueprint->toSql($this->getConnection(), $this->getGrammar()); - - $this->assertCount(1, $statements); - $this->assertEquals('alter table "users" add "foo" varbinary(max) not null', $statements[0]); - } - - - protected function getConnection() - { - return m::mock(Connection::class); - } - - - public function getGrammar() - { - return new Illuminate\Database\Schema\Grammars\SqlServerGrammar; - } - -} diff --git a/tests/Database/stubs/EloquentModelNamespacedStub.php b/tests/Database/stubs/EloquentModelNamespacedStub.php deleted file mode 100755 index 39872488..00000000 --- a/tests/Database/stubs/EloquentModelNamespacedStub.php +++ /dev/null @@ -1,7 +0,0 @@ -getEncrypter(); - $this->assertNotEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $e->encrypt('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')); - $encrypted = $e->encrypt('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); - $this->assertEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $e->decrypt($encrypted)); - } - - - public function testEncryptionWithCustomCipher() - { - $e = $this->getEncrypter(); - $this->assertNotEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $e->encrypt('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')); - $encrypted = $e->encrypt('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); - $this->assertEquals('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $e->decrypt($encrypted)); - } - - public function testExceptionThrownWhenPayloadIsInvalid() - { - $this->expectException(Illuminate\Contracts\Encryption\DecryptException::class); - $this->expectExceptionMessage("The payload is invalid."); - $e = $this->getEncrypter(); - $payload = $e->encrypt('foo'); - $payload = str_shuffle((string) $payload); - $e->decrypt($payload); - } - - - protected function getEncrypter() - { - return new Encrypter(str_repeat('a', 32)); - } - -} diff --git a/tests/Events/EventsDispatcherTest.php b/tests/Events/EventsDispatcherTest.php deleted file mode 100755 index c98c2916..00000000 --- a/tests/Events/EventsDispatcherTest.php +++ /dev/null @@ -1,133 +0,0 @@ -listen( - 'foo', - function ($foo) { - $_SERVER['__event.test'] = $foo; - } - ); - $d->fire('foo', ['bar']); - $this->assertEquals('bar', $_SERVER['__event.test']); - } - - - public function testDispatchIsCanonicalAndFireDelegates() - { - $d = new Dispatcher; - $d->listen('foo', function ($x) { return 'heard:'.$x; }); - - $this->assertSame(['heard:bar'], $d->dispatch('foo', ['bar'])); - // fire() is the L4.2 alias — identical behaviour, removed at L13 swap - $this->assertSame($d->dispatch('foo', ['bar']), $d->fire('foo', ['bar'])); - // halt returns the first non-null response - $this->assertSame('heard:bar', $d->dispatch('foo', ['bar'], true)); - } - - - public function testContainerResolutionOfEventHandlers() - { - $d = new Dispatcher($container = m::mock(Container::class)); - $container->shouldReceive('make')->once()->with('FooHandler')->andReturn($handler = m::mock('StdClass')); - $handler->shouldReceive('onFooEvent')->once()->with('foo', 'bar'); - $d->listen('foo', 'FooHandler@onFooEvent'); - $d->fire('foo', ['foo', 'bar']); - } - - - public function testContainerResolutionOfEventHandlersWithDefaultMethods() - { - $d = new Dispatcher($container = m::mock(Container::class)); - $container->shouldReceive('make')->once()->with('FooHandler')->andReturn($handler = m::mock('StdClass')); - $handler->shouldReceive('handle')->once()->with('foo', 'bar'); - $d->listen('foo', 'FooHandler'); - $d->fire('foo', ['foo', 'bar']); - } - - - public function testQueuedEventsAreFired() - { - unset($_SERVER['__event.test']); - $d = new Dispatcher; - $d->queue('update', ['name' => 'taylor']); - $d->listen('update', function($name) - { - $_SERVER['__event.test'] = $name; - }); - - $this->assertFalse(isset($_SERVER['__event.test'])); - $d->flush('update'); - $this->assertEquals('taylor', $_SERVER['__event.test']); - } - - - public function testQueuedEventsCanBeForgotten() - { - $_SERVER['__event.test'] = 'unset'; - $d = new Dispatcher; - $d->queue('update', ['name' => 'taylor']); - $d->listen('update', function($name) - { - $_SERVER['__event.test'] = $name; - }); - - $d->forgetQueued(); - $d->flush('update'); - $this->assertEquals('unset', $_SERVER['__event.test']); - } - - - public function testWildcardListeners() - { - unset($_SERVER['__event.test']); - $d = new Dispatcher; - $d->listen('foo.bar', function() { $_SERVER['__event.test'] = 'regular'; }); - $d->listen('foo.*', function() { $_SERVER['__event.test'] = 'wildcard'; }); - $d->listen('bar.*', function() { $_SERVER['__event.test'] = 'nope'; }); - $d->fire('foo.bar'); - - $this->assertEquals('wildcard', $_SERVER['__event.test']); - } - - - public function testListenersCanBeRemoved() - { - unset($_SERVER['__event.test']); - $d = new Dispatcher; - $d->listen('foo', function() { $_SERVER['__event.test'] = 'foo'; }); - $d->forget('foo'); - $d->fire('foo'); - - $this->assertFalse(isset($_SERVER['__event.test'])); - } - - - public function testFiringReturnsCurrentlyFiredEvent() - { - unset($_SERVER['__event.test']); - $d = new Dispatcher; - $d->listen('foo', function() use ($d) { $_SERVER['__event.test'] = $d->firing(); $d->fire('bar'); }); - $d->listen('bar', function() use ($d) { $_SERVER['__event.test'] = $d->firing(); }); - $d->fire('foo'); - - $this->assertEquals('bar', $_SERVER['__event.test']); - } - -} diff --git a/tests/Filesystem/FilesystemTest.php b/tests/Filesystem/FilesystemTest.php deleted file mode 100755 index 4e671f52..00000000 --- a/tests/Filesystem/FilesystemTest.php +++ /dev/null @@ -1,130 +0,0 @@ -assertEquals('Hello World', $files->get(__DIR__.'/file.txt')); - @unlink(__DIR__.'/file.txt'); - } - - - public function testPutStoresFiles() - { - $files = new Filesystem; - $files->put(__DIR__.'/file.txt', 'Hello World'); - $this->assertEquals('Hello World', file_get_contents(__DIR__.'/file.txt')); - @unlink(__DIR__.'/file.txt'); - } - - - public function testDeleteRemovesFiles() - { - file_put_contents(__DIR__.'/file.txt', 'Hello World'); - $files = new Filesystem; - $files->delete(__DIR__.'/file.txt'); - $this->assertFileDoesNotExist(__DIR__ . '/file.txt'); - @unlink(__DIR__.'/file.txt'); - } - - - public function testPrependExistingFiles() - { - $files = new Filesystem; - $files->put(__DIR__.'/file.txt', 'World'); - $files->prepend(__DIR__.'/file.txt', 'Hello '); - $this->assertEquals('Hello World', file_get_contents(__DIR__.'/file.txt')); - @unlink(__DIR__.'/file.txt'); - } - - - public function testPrependNewFiles() - { - $files = new Filesystem; - $files->prepend(__DIR__.'/file.txt', 'Hello World'); - $this->assertEquals('Hello World', file_get_contents(__DIR__.'/file.txt')); - @unlink(__DIR__.'/file.txt'); - } - - - public function testDeleteDirectory() - { - mkdir(__DIR__.'/foo'); - file_put_contents(__DIR__.'/foo/file.txt', 'Hello World'); - $files = new Filesystem; - $files->deleteDirectory(__DIR__.'/foo'); - $this->assertDirectoryDoesNotExist(__DIR__ . '/foo'); - $this->assertFileDoesNotExist(__DIR__ . '/foo/file.txt'); - } - - - public function testCleanDirectory() - { - mkdir(__DIR__.'/foo'); - file_put_contents(__DIR__.'/foo/file.txt', 'Hello World'); - $files = new Filesystem; - $files->cleanDirectory(__DIR__.'/foo'); - $this->assertDirectoryExists(__DIR__ . '/foo'); - $this->assertFileDoesNotExist(__DIR__ . '/foo/file.txt'); - @rmdir(__DIR__.'/foo'); - } - - - public function testFilesMethod() - { - mkdir(__DIR__.'/foo'); - file_put_contents(__DIR__.'/foo/1.txt', '1'); - file_put_contents(__DIR__.'/foo/2.txt', '2'); - mkdir(__DIR__.'/foo/bar'); - $files = new Filesystem; - $this->assertEquals([__DIR__.'/foo/1.txt', __DIR__.'/foo/2.txt'], $files->files(__DIR__.'/foo')); - unset($files); - @unlink(__DIR__.'/foo/1.txt'); - @unlink(__DIR__.'/foo/2.txt'); - @rmdir(__DIR__.'/foo/bar'); - @rmdir(__DIR__.'/foo'); - } - - - public function testCopyDirectoryReturnsFalseIfSourceIsntDirectory() - { - $files = new Filesystem; - $this->assertFalse($files->copyDirectory(__DIR__.'/foo/bar/baz/breeze/boom', __DIR__)); - } - - - public function testCopyDirectoryMovesEntireDirectory() - { - mkdir(__DIR__.'/tmp', 0777, true); - file_put_contents(__DIR__.'/tmp/foo.txt', ''); - file_put_contents(__DIR__.'/tmp/bar.txt', ''); - mkdir(__DIR__.'/tmp/nested', 0777, true); - file_put_contents(__DIR__.'/tmp/nested/baz.txt', ''); - - $files = new Filesystem; - $files->copyDirectory(__DIR__.'/tmp', __DIR__.'/tmp2'); - $this->assertDirectoryExists(__DIR__ . '/tmp2'); - $this->assertFileExists(__DIR__ . '/tmp2/foo.txt'); - $this->assertFileExists(__DIR__ . '/tmp2/bar.txt'); - $this->assertDirectoryExists(__DIR__ . '/tmp2/nested'); - $this->assertFileExists(__DIR__ . '/tmp2/nested/baz.txt'); - - unlink(__DIR__.'/tmp/nested/baz.txt'); - rmdir(__DIR__.'/tmp/nested'); - unlink(__DIR__.'/tmp/bar.txt'); - unlink(__DIR__.'/tmp/foo.txt'); - rmdir(__DIR__.'/tmp'); - - unlink(__DIR__.'/tmp2/nested/baz.txt'); - rmdir(__DIR__.'/tmp2/nested'); - unlink(__DIR__.'/tmp2/foo.txt'); - unlink(__DIR__.'/tmp2/bar.txt'); - rmdir(__DIR__.'/tmp2'); - } - -} diff --git a/tests/Http/HttpJsonResponseTest.php b/tests/Http/HttpJsonResponseTest.php deleted file mode 100644 index 6f79a83d..00000000 --- a/tests/Http/HttpJsonResponseTest.php +++ /dev/null @@ -1,34 +0,0 @@ - 'bar']); - $data = $response->getData(); - $this->assertInstanceOf('StdClass', $data); - $this->assertEquals('bar', $data->foo); - } - - - public function testSetAndRetrieveOptions() - { - $response = new Illuminate\Http\JsonResponse(['foo' => 'bar']); - $response->setJsonOptions(JSON_PRETTY_PRINT); - $this->assertSame(JSON_PRETTY_PRINT, $response->getJsonOptions()); - } - - - public function testSetAndRetrieveStatusCode() - { - $response = new Illuminate\Http\JsonResponse(['foo' => 'bar'], 404); - $this->assertSame(404, $response->getStatusCode()); - - $response = new Illuminate\Http\JsonResponse(['foo' => 'bar']); - $response->setStatusCode(404); - $this->assertSame(404, $response->getStatusCode()); - } - -} diff --git a/tests/Http/HttpRedirectResponseTest.php b/tests/Http/HttpRedirectResponseTest.php deleted file mode 100755 index 5c2fde8e..00000000 --- a/tests/Http/HttpRedirectResponseTest.php +++ /dev/null @@ -1,142 +0,0 @@ -assertNull($response->headers->get('foo')); - $response->header('foo', 'bar'); - $this->assertEquals('bar', $response->headers->get('foo')); - $response->header('foo', 'baz', false); - $this->assertEquals('bar', $response->headers->get('foo')); - $response->header('foo', 'baz'); - $this->assertEquals('baz', $response->headers->get('foo')); - } - - - public function testWithOnRedirect() -{ - $response = new RedirectResponse('foo.bar'); - $response->setRequest(Request::create('/', 'GET', ['name' => 'Taylor', 'age' => 26])); - $response->setSession($session = m::mock(Store::class)); - $session->shouldReceive('flash')->twice(); - $response->with(['name', 'age']); - } - - - public function testWithCookieOnRedirect() - { - $response = new RedirectResponse('foo.bar'); - $this->assertCount(0, $response->headers->getCookies()); - $this->assertEquals($response, $response->withCookie(new Cookie('foo', 'bar'))); - $cookies = $response->headers->getCookies(); - $this->assertCount(1, $cookies); - $this->assertEquals('foo', $cookies[0]->getName()); - $this->assertEquals('bar', $cookies[0]->getValue()); - } - - - public function testInputOnRedirect() - { - $response = new RedirectResponse('foo.bar'); - $response->setRequest(Request::create('/', 'GET', ['name' => 'Taylor', 'age' => 26])); - $response->setSession($session = m::mock(Store::class)); - $session->shouldReceive('flashInput')->once()->with(['name' => 'Taylor', 'age' => 26]); - $response->withInput(); - } - - - public function testOnlyInputOnRedirect() - { - $response = new RedirectResponse('foo.bar'); - $response->setRequest(Request::create('/', 'GET', ['name' => 'Taylor', 'age' => 26])); - $response->setSession($session = m::mock(Store::class)); - $session->shouldReceive('flashInput')->once()->with(['name' => 'Taylor']); - $response->onlyInput('name'); - } - - - public function testExceptInputOnRedirect() - { - $response = new RedirectResponse('foo.bar'); - $response->setRequest(Request::create('/', 'GET', ['name' => 'Taylor', 'age' => 26])); - $response->setSession($session = m::mock(Store::class)); - $session->shouldReceive('flashInput')->once()->with(['name' => 'Taylor']); - $response->exceptInput('age'); - } - - - public function testFlashingErrorsOnRedirect() - { - $response = new RedirectResponse('foo.bar'); - $response->setRequest(Request::create('/', 'GET', ['name' => 'Taylor', 'age' => 26])); - $response->setSession($session = m::mock(Store::class)); - $session->shouldReceive('get')->with('errors', m::type(ViewErrorBag::class))->andReturn(new Illuminate\Support\ViewErrorBag); - $session->shouldReceive('flash')->once()->with('errors', m::type(ViewErrorBag::class)); - $provider = m::mock(MessageProviderInterface::class); - $provider->shouldReceive('getMessageBag')->once()->andReturn(new Illuminate\Support\MessageBag); - $response->withErrors($provider); - } - - - public function testSettersGettersOnRequest() - { - $response = new RedirectResponse('foo.bar'); - $this->assertNull($response->getRequest()); - $this->assertNull($response->getSession()); - - $request = Request::create('/', 'GET'); - $session = m::mock(Store::class); - $response->setRequest($request); - $response->setSession($session); - $this->assertSame($request, $response->getRequest()); - $this->assertSame($session, $response->getSession()); - } - - - public function testRedirectWithErrorsArrayConvertsToMessageBag() - { - $response = new RedirectResponse('foo.bar'); - $response->setRequest(Request::create('/', 'GET', ['name' => 'Taylor', 'age' => 26])); - $response->setSession($session = m::mock(Store::class)); - $session->shouldReceive('get')->with('errors', m::type(ViewErrorBag::class))->andReturn(new Illuminate\Support\ViewErrorBag); - $session->shouldReceive('flash')->once()->with('errors', m::type(ViewErrorBag::class)); - $provider = ['foo' => 'bar']; - $response->withErrors($provider); - } - - - public function testMagicCall() - { - $response = new RedirectResponse('foo.bar'); - $response->setRequest(Request::create('/', 'GET', ['name' => 'Taylor', 'age' => 26])); - $response->setSession($session = m::mock(Store::class)); - $session->shouldReceive('flash')->once()->with('foo', 'bar'); - $response->withFoo('bar'); - } - - - public function testMagicCallException() - { - $this->expectException('BadMethodCallException'); - $response = new RedirectResponse('foo.bar'); - $response->doesNotExist('bar'); - } - -} diff --git a/tests/Http/HttpRequestTest.php b/tests/Http/HttpRequestTest.php deleted file mode 100755 index 656b8373..00000000 --- a/tests/Http/HttpRequestTest.php +++ /dev/null @@ -1,428 +0,0 @@ -assertSame($request, $request->instance()); - } - - - public function testRootMethod() - { - $request = Request::create('http://example.com/foo/bar/script.php?test'); - $this->assertEquals('http://example.com', $request->root()); - } - - - public function testPathMethod() - { - $request = Request::create('', 'GET'); - $this->assertEquals('/', $request->path()); - - $request = Request::create('/foo/bar', 'GET'); - $this->assertEquals('foo/bar', $request->path()); - } - - - public function testDecodedPathMethod() - { - $request = Request::create('/foo%20bar'); - $this->assertEquals('foo bar', $request->decodedPath()); - } - - - /** - * @dataProvider segmentProvider - */ - public function testSegmentMethod($path, $segment, $expected) - { - $request = Request::create($path, 'GET'); - $this->assertEquals($expected, $request->segment($segment, 'default')); - } - - - public function segmentProvider() - { - return [ - ['', 1, 'default'], - ['foo/bar//baz', '1', 'foo'], - ['foo/bar//baz', '2', 'bar'], - ['foo/bar//baz', '3', 'baz'], - ]; - } - - /** - * @dataProvider segmentsProvider - */ - public function testSegmentsMethod($path, $expected) - { - $request = Request::create($path, 'GET'); - $this->assertEquals($expected, $request->segments()); - - $request = Request::create('foo/bar', 'GET'); - $this->assertEquals(['foo', 'bar'], $request->segments()); - } - - - public function segmentsProvider() - { - return [ - ['', []], - ['foo/bar', ['foo', 'bar']], - ['foo/bar//baz', ['foo', 'bar', 'baz']], - ['foo/0/bar', ['foo', '0', 'bar']], - ]; - } - - - public function testUrlMethod() - { - $request = Request::create('http://foo.com/foo/bar?name=taylor', 'GET'); - $this->assertEquals('http://foo.com/foo/bar', $request->url()); - - $request = Request::create('http://foo.com/foo/bar/?', 'GET'); - $this->assertEquals('http://foo.com/foo/bar', $request->url()); - } - - - public function testFullUrlMethod() - { - $request = Request::create('http://foo.com/foo/bar?name=taylor', 'GET'); - $this->assertEquals('http://foo.com/foo/bar?name=taylor', $request->fullUrl()); - - $request = Request::create('https://foo.com', 'GET'); - $this->assertEquals('https://foo.com', $request->fullUrl()); - } - - - public function testIsMethod() - { - $request = Request::create('/foo/bar', 'GET'); - - $this->assertTrue($request->is('foo*')); - $this->assertFalse($request->is('bar*')); - $this->assertTrue($request->is('*bar*')); - $this->assertTrue($request->is('bar*', 'foo*', 'baz')); - - $request = Request::create('/', 'GET'); - - $this->assertTrue($request->is('/')); - } - - - public function testAjaxMethod() - { - $request = Request::create('/', 'GET'); - $this->assertFalse($request->ajax()); - $request = Request::create('/', 'GET', [], [], [], ['HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'], '{}'); - $this->assertTrue($request->ajax()); - } - - - public function testSecureMethod() - { - $request = Request::create('http://example.com', 'GET'); - $this->assertFalse($request->secure()); - $request = Request::create('https://example.com', 'GET'); - $this->assertTrue($request->secure()); - } - - - public function testHasMethod() - { - $request = Request::create('/', 'GET', ['name' => 'Taylor']); - $this->assertTrue($request->has('name')); - $this->assertFalse($request->has('foo')); - $this->assertFalse($request->has('name', 'email')); - - $request = Request::create('/', 'GET', ['name' => 'Taylor', 'email' => 'foo']); - $this->assertTrue($request->has('name')); - $this->assertTrue($request->has('name', 'email')); - - //test arrays within query string - $request = Request::create('/', 'GET', ['foo' => ['bar', 'baz']]); - $this->assertTrue($request->has('foo')); - } - - - public function testInputMethod() - { - $request = Request::create('/', 'GET', ['name' => 'Taylor']); - $this->assertEquals('Taylor', $request->input('name')); - $this->assertEquals('Bob', $request->input('foo', 'Bob')); - } - - - public function testOnlyMethod() - { - $request = Request::create('/', 'GET', ['name' => 'Taylor', 'age' => 25]); - $this->assertEquals(['age' => 25], $request->only('age')); - $this->assertEquals(['name' => 'Taylor', 'age' => 25], $request->only('name', 'age')); - - $request = Request::create('/', 'GET', ['developer' => ['name' => 'Taylor', 'age' => 25]]); - $this->assertEquals(['developer' => ['age' => 25]], $request->only('developer.age')); - $this->assertEquals(['developer' => ['name' => 'Taylor'], 'test' => null], $request->only('developer.name', 'test')); - } - - - public function testExceptMethod() - { - $request = Request::create('/', 'GET', ['name' => 'Taylor', 'age' => 25]); - $this->assertEquals(['name' => 'Taylor'], $request->except('age')); - $this->assertEquals([], $request->except('age', 'name')); - } - - - public function testQueryMethod() - { - $request = Request::create('/', 'GET', ['name' => 'Taylor']); - $this->assertEquals('Taylor', $request->query('name')); - $this->assertEquals('Bob', $request->query('foo', 'Bob')); - $all = $request->query(null); - $this->assertEquals('Taylor', $all['name']); - - $request = Request::create('/', 'GET', ['hello' => 'world', 'user' => ['Taylor', 'Mohamed Said']]); - $this->assertSame(['Taylor', 'Mohamed Said'], $request->query('user')); - $this->assertSame(['hello' => 'world', 'user' => ['Taylor', 'Mohamed Said']], $request->query->all()); - - $request = Request::create('/?hello=world&user[]=Taylor&user[]=Mohamed%20Said', 'GET', []); - $this->assertSame(['Taylor', 'Mohamed Said'], $request->query('user')); - $this->assertSame(['hello' => 'world', 'user' => ['Taylor', 'Mohamed Said']], $request->query->all()); - } - - - public function testCookieMethod() - { - $request = Request::create('/', 'GET', [], ['name' => 'Taylor']); - $this->assertEquals('Taylor', $request->cookie('name')); - $this->assertEquals('Bob', $request->cookie('foo', 'Bob')); - $all = $request->cookie(null); - $this->assertEquals('Taylor', $all['name']); - } - - - public function testHasCookieMethod() - { - $request = Request::create('/', 'GET', [], ['foo' => 'bar']); - $this->assertTrue($request->hasCookie('foo')); - $this->assertFalse($request->hasCookie('qu')); - } - - - public function testFileMethod() - { - $files = [ - 'foo' => [ - 'size' => 500, - 'name' => 'foo.jpg', - 'tmp_name' => __FILE__, - 'type' => 'blah', - 'error' => null, - ], - ]; - $request = Request::create('/', 'GET', [], [], $files); - $this->assertInstanceOf(UploadedFile::class, $request->file('foo')); - } - - - public function testHasFileMethod() - { - $request = Request::create('/', 'GET', [], [], []); - $this->assertFalse($request->hasFile('foo')); - - $files = [ - 'foo' => [ - 'size' => 500, - 'name' => 'foo.jpg', - 'tmp_name' => __FILE__, - 'type' => 'blah', - 'error' => null, - ], - ]; - $request = Request::create('/', 'GET', [], [], $files); - $this->assertTrue($request->hasFile('foo')); - } - - - public function testServerMethod() - { - $request = Request::create('/', 'GET', [], [], [], ['foo' => 'bar']); - $this->assertEquals('bar', $request->server('foo')); - $this->assertEquals('bar', $request->server('foo.doesnt.exist', 'bar')); - $all = $request->server(null); - $this->assertEquals('bar', $all['foo']); - } - - - public function testMergeMethod() - { - $request = Request::create('/', 'GET', ['name' => 'Taylor']); - $merge = ['buddy' => 'Dayle']; - $request->merge($merge); - $this->assertEquals('Taylor', $request->input('name')); - $this->assertEquals('Dayle', $request->input('buddy')); - } - - - public function testReplaceMethod() - { - $request = Request::create('/', 'GET', ['name' => 'Taylor']); - $replace = ['buddy' => 'Dayle']; - $request->replace($replace); - $this->assertNull($request->input('name')); - $this->assertEquals('Dayle', $request->input('buddy')); - } - - - public function testHeaderMethod() - { - $request = Request::create('/', 'GET', [], [], [], ['HTTP_DO_THIS' => 'foo']); - $this->assertEquals('foo', $request->header('do-this')); - $all = $request->header(null); - $this->assertEquals('foo', $all['do-this'][0]); - } - - - public function testJSONMethod() - { - $payload = ['name' => 'taylor']; - $request = Request::create('/', 'GET', [], [], [], ['CONTENT_TYPE' => 'application/json'], json_encode($payload)); - $this->assertEquals('taylor', $request->json('name')); - $this->assertEquals('taylor', $request->input('name')); - $data = $request->json()->all(); - $this->assertEquals($payload, $data); - } - - - public function testJSONEmulatingPHPBuiltInServer() - { - $payload = ['name' => 'taylor']; - $content = json_encode($payload); - // The built in PHP 5.4 webserver incorrectly provides HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH, - // rather than CONTENT_TYPE and CONTENT_LENGTH - $request = Request::create('/', 'GET', [], [], [], ['HTTP_CONTENT_TYPE' => 'application/json', 'HTTP_CONTENT_LENGTH' => strlen($content)], $content); - $this->assertTrue($request->isJson()); - $data = $request->json()->all(); - $this->assertEquals($payload, $data); - - $data = $request->all(); - $this->assertEquals($payload, $data); - } - - - public function testAllInputReturnsInputAndFiles() - { - $file = $this->getMock(UploadedFile::class, null, [__FILE__, 'photo.jpg']); - $request = Request::create('/?boom=breeze', 'GET', ['foo' => 'bar'], [], ['baz' => $file]); - $this->assertEquals(['foo' => 'bar', 'baz' => $file, 'boom' => 'breeze'], $request->all()); - } - - - public function testAllInputReturnsNestedInputAndFiles() - { - $file = $this->getMock(UploadedFile::class, null, [__FILE__, 'photo.jpg']); - $request = Request::create('/?boom=breeze', 'GET', ['foo' => ['bar' => 'baz']], [], ['foo' => ['photo' => $file]] - ); - $this->assertEquals(['foo' => ['bar' => 'baz', 'photo' => $file], 'boom' => 'breeze'], $request->all()); - } - - - public function testAllInputReturnsInputAfterReplace() - { - $request = Request::create('/?boom=breeze', 'GET', ['foo' => ['bar' => 'baz']]); - $request->replace(['foo' => ['bar' => 'baz'], 'boom' => 'breeze']); - $this->assertEquals(['foo' => ['bar' => 'baz'], 'boom' => 'breeze'], $request->all()); - } - - - public function testAllInputWithNumericKeysReturnsInputAfterReplace() - { - $request1 = Request::create('/', 'POST', [0 => 'A', 1 => 'B', 2 => 'C']); - $request1->replace([0 => 'A', 1 => 'B', 2 => 'C']); - $this->assertEquals([0 => 'A', 1 => 'B', 2 => 'C'], $request1->all()); - - $request2 = Request::create('/', 'POST', [1 => 'A', 2 => 'B', 3 => 'C']); - $request2->replace([1 => 'A', 2 => 'B', 3 => 'C']); - $this->assertEquals([1 => 'A', 2 => 'B', 3 => 'C'], $request2->all()); - } - - - public function testOldMethodCallsSession() - { - $request = Request::create('/', 'GET'); - $session = m::mock(Store::class); - $session->shouldReceive('getOldInput')->once()->with('foo', 'bar')->andReturn('boom'); - $request->setLaravelSession($session); - $this->assertEquals('boom', $request->old('foo', 'bar')); - } - - - public function testFlushMethodCallsSession() - { - $request = Request::create('/', 'GET'); - $session = m::mock(Store::class); - $session->shouldReceive('flashInput')->once(); - $request->setLaravelSession($session); - $request->flush(); - } - - - public function testFormatReturnsAcceptableFormat() - { - $request = Request::create('/', 'GET', [], [], [], ['HTTP_ACCEPT' => 'application/json']); - $this->assertEquals('json', $request->format()); - $this->assertTrue($request->wantsJson()); - - $request = Request::create('/', 'GET', [], [], [], ['HTTP_ACCEPT' => 'application/atom+xml']); - $this->assertEquals('atom', $request->format()); - $this->assertFalse($request->wantsJson()); - - $request = Request::create('/', 'GET', [], [], [], ['HTTP_ACCEPT' => 'is/not/known']); - $this->assertEquals('html', $request->format()); - $this->assertEquals('foo', $request->format('foo')); - } - - - public function testSessionMethod() - { - $this->expectException('RuntimeException'); - $request = Request::create('/', 'GET'); - $request->session(); - } - - - public function testCreateFromBase() - { - $body = [ - 'foo' => 'bar', - 'baz' => ['qux'], - ]; - - $server = [ - 'CONTENT_TYPE' => 'application/json', - ]; - - $base = SymfonyRequest::create('/', 'GET', [], [], [], $server, json_encode($body)); - - $request = Request::createFromBase($base); - - $this->assertEquals($request->request->all(), $body); - } - -} diff --git a/tests/Http/HttpResponseTest.php b/tests/Http/HttpResponseTest.php deleted file mode 100755 index d245e311..00000000 --- a/tests/Http/HttpResponseTest.php +++ /dev/null @@ -1,143 +0,0 @@ -assertSame('{"foo":"bar"}', $response->getContent()); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - - $response = new Response(new JsonableStub); - $this->assertSame('foo', $response->getContent()); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - - $response = new Response(new ArrayableAndJsonableStub); - $this->assertSame('{"foo":"bar"}', $response->getContent()); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - - $response = new Response; - $response->setContent(['foo' => 'bar']); - $this->assertSame('{"foo":"bar"}', $response->getContent()); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - - $response = new Response(new JsonSerializableStub); - $this->assertSame('{"foo":"bar"}', $response->getContent()); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - - $response = new Response(new ArrayableStub); - $this->assertSame('{"foo":"bar"}', $response->getContent()); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - - $response->setContent('{"foo": "bar"}'); - $this->assertSame('{"foo": "bar"}', $response->getContent()); - $this->assertSame('application/json', $response->headers->get('Content-Type')); - } - - - public function testRenderablesAreRendered() - { - $mock = m::mock(Renderable::class); - $mock->shouldReceive('render')->once()->andReturn('foo'); - $response = new Response($mock); - $this->assertEquals('foo', $response->getContent()); - } - - - public function testHeader() - { - $response = new Response(); - $this->assertNull($response->headers->get('foo')); - $response->header('foo', 'bar'); - $this->assertEquals('bar', $response->headers->get('foo')); - $response->header('foo', 'baz', false); - $this->assertEquals('bar', $response->headers->get('foo')); - $response->header('foo', 'baz'); - $this->assertEquals('baz', $response->headers->get('foo')); - } - - - public function testWithCookie() - { - $response = new Response(); - $this->assertCount(0, $response->headers->getCookies()); - $this->assertEquals($response, $response->withCookie(new Cookie('foo', 'bar'))); - $cookies = $response->headers->getCookies(); - $this->assertCount(1, $cookies); - $this->assertEquals('foo', $cookies[0]->getName()); - $this->assertEquals('bar', $cookies[0]->getValue()); - } - - - public function testGetOriginalContent() - { - $arr = ['foo' => 'bar']; - $response = new Response(); - $response->setContent($arr); - $this->assertSame($arr, $response->getOriginalContent()); - } - - - public function testSetAndRetrieveStatusCode() - { - $response = new Response('foo', 404); - $this->assertSame(404, $response->getStatusCode()); - - $response = new Response('foo'); - $response->setStatusCode(404); - $this->assertSame(404, $response->getStatusCode()); - } - -} - -class ArrayableStub implements ArrayableInterface -{ - public function toArray() - { - return ['foo' => 'bar']; - } -} - -class ArrayableAndJsonableStub implements ArrayableInterface, JsonableInterface -{ - public function toJson($options = 0) - { - return '{"foo":"bar"}'; - } - - public function toArray() - { - return []; - } -} - -class JsonableStub implements JsonableInterface -{ - public function toJson($options = 0) - { - return 'foo'; - } -} - -class JsonSerializableStub implements JsonSerializable -{ - public function jsonSerialize(): array - { - return ['foo' => 'bar']; - } -} \ No newline at end of file diff --git a/tests/Session/SessionMiddlewareTest.php b/tests/Session/SessionMiddlewareTest.php deleted file mode 100644 index 99f74221..00000000 --- a/tests/Session/SessionMiddlewareTest.php +++ /dev/null @@ -1,86 +0,0 @@ -shouldReceive('getSessionConfig')->andReturn([ - 'driver' => 'file', - 'lottery' => [100, 100], - 'path' => '/', - 'domain' => null, - 'lifetime' => 120, - 'expire_on_close' => false, - ]); - - $manager->shouldReceive('driver')->andReturn($driver = m::mock(Store::class)->makePartial()); - $driver->shouldReceive('setRequestOnHandler')->once()->with($request); - $driver->shouldReceive('start')->once(); - $app->shouldReceive('handle')->once()->with($request, Symfony\Component\HttpKernel\HttpKernelInterface::MAIN_REQUEST, true)->andReturn($response); - $driver->shouldReceive('save')->once(); - $driver->shouldReceive('getHandler')->andReturn($handler = m::mock('StdClass')); - $handler->shouldReceive('gc')->once()->with(120 * 60); - $driver->shouldReceive('getName')->andReturn('name'); - $driver->shouldReceive('getId')->andReturn(1); - $driver->shouldReceive('setPreviousUrl')->with('http://www.foo.com/some')->once(); - - $middleResponse = $middle->handle($request); - - $this->assertSame($response, $middleResponse); - $this->assertEquals(1, head($response->headers->getCookies())->getValue()); - } - - - public function testSessionIsNotUsedWhenNoDriver() - { - $request = Symfony\Component\HttpFoundation\Request::create('/', 'GET'); - $response = new Symfony\Component\HttpFoundation\Response; - $middle = new Illuminate\Session\Middleware( - $app = m::mock(HttpKernelInterface::class), - $manager = m::mock(SessionManager::class) - ); - $manager->shouldReceive('getSessionConfig')->andReturn([ - 'driver' => null, - ]); - $app->shouldReceive('handle')->once()->with($request, Symfony\Component\HttpKernel\HttpKernelInterface::MAIN_REQUEST, true)->andReturn($response); - $middleResponse = $middle->handle($request); - - $this->assertSame($response, $middleResponse); - } - - - public function testCheckingForRequestUsingArraySessions() - { - $middleware = new Illuminate\Session\Middleware( - m::mock(HttpKernelInterface::class), - $manager = m::mock(SessionManager::class), - function() { return true; } - ); - - $manager->shouldReceive('setDefaultDriver')->once()->with('array'); - - $middleware->checkRequestForArraySessions(new Symfony\Component\HttpFoundation\Request); - } - -} diff --git a/tests/Session/SessionStoreTest.php b/tests/Session/SessionStoreTest.php deleted file mode 100644 index 85553fcd..00000000 --- a/tests/Session/SessionStoreTest.php +++ /dev/null @@ -1,329 +0,0 @@ -getSession(); - $session->getHandler()->shouldReceive('read')->once()->with($this->getSessionId())->andReturn( - serialize(['foo' => 'bar', 'bagged' => ['name' => 'taylor']]) - ); - $session->start(); - - $this->assertEquals('bar', $session->get('foo')); - $this->assertEquals('baz', $session->get('bar', 'baz')); - $this->assertTrue($session->has('foo')); - $this->assertFalse($session->has('bar')); - $this->assertTrue($session->isStarted()); - - $session->put('baz', 'boom'); - $this->assertTrue($session->has('baz')); - } - - - public function testExists() - { - $session = $this->getSession(); - $session->put('foo', 'bar'); - $session->put('baz', null); - - $this->assertTrue($session->exists('foo')); - $this->assertTrue($session->exists('baz')); - $this->assertTrue($session->exists(['foo', 'baz'])); - $this->assertFalse($session->exists(['foo', 'bar'])); - $this->assertFalse($session->exists('bar')); - - $this->assertTrue($session->has('foo')); - $this->assertFalse($session->has('baz')); - } - - - public function testSessionMigration() - { - $session = $this->getSession(); - $oldId = $session->getId(); - $session->getHandler()->shouldReceive('destroy')->never(); - $this->assertTrue($session->migrate()); - $this->assertNotEquals($oldId, $session->getId()); - - - $session = $this->getSession(); - $oldId = $session->getId(); - $session->getHandler()->shouldReceive('destroy')->once()->with($oldId); - $this->assertTrue($session->migrate(true)); - $this->assertNotEquals($oldId, $session->getId()); - } - - - public function testSessionRegeneration() - { - $session = $this->getSession(); - $oldId = $session->getId(); - $session->getHandler()->shouldReceive('destroy')->never(); - $this->assertTrue($session->regenerate()); - $this->assertNotEquals($oldId, $session->getId()); - } - - - public function testCantSetInvalidId() - { - $session = $this->getSession(); - - $session->setId(null); - $this->assertFalse(null == $session->getId()); - - $session->setId(['a']); - $this->assertFalse(['a'] == $session->getId()); - - $session->setId('wrong'); - $this->assertFalse('wrong' == $session->getId()); - } - - - public function testSessionInvalidate() - { - $session = $this->getSession(); - $oldId = $session->getId(); - $session->set('foo','bar'); - $this->assertGreaterThan(0, count($session->all())); - $session->getHandler()->shouldReceive('destroy')->never(); - $this->assertTrue($session->invalidate()); - $this->assertNotEquals($oldId, $session->getId()); - $this->assertCount(0, $session->all()); - } - - - public function testSessionIsProperlySaved() - { - $session = $this->getSession(); - $session->getHandler()->shouldReceive('read')->once()->andReturn(serialize([])); - $session->start(); - $session->put('foo', 'bar'); - $session->flash('baz', 'boom'); - $session->getHandler()->shouldReceive('write')->once()->with( - $this->getSessionId(), - serialize([ - '_token' => $session->token(), - 'foo' => 'bar', - 'baz' => 'boom', - 'flash' => [ - 'new' => [], - 'old' => ['baz'], - ], - ]) - ); - $session->save(); - - $this->assertFalse($session->isStarted()); - } - - - public function testOldInputFlashing() - { - $session = $this->getSession(); - $session->put('boom', 'baz'); - $session->flashInput(['foo' => 'bar', 'bar' => 0]); - - $this->assertTrue($session->hasOldInput('foo')); - $this->assertEquals('bar', $session->getOldInput('foo')); - $this->assertEquals(0, $session->getOldInput('bar')); - $this->assertFalse($session->hasOldInput('boom')); - - $session->ageFlashData(); - - $this->assertTrue($session->hasOldInput('foo')); - $this->assertEquals('bar', $session->getOldInput('foo')); - $this->assertEquals(0, $session->getOldInput('bar')); - $this->assertFalse($session->hasOldInput('boom')); - } - - - public function testDataFlashing() - { - $session = $this->getSession(); - $session->flash('foo', 'bar'); - $session->flash('bar', 0); - - $this->assertTrue($session->has('foo')); - $this->assertEquals('bar', $session->get('foo')); - $this->assertEquals(0, $session->get('bar')); - - $session->ageFlashData(); - - $this->assertTrue($session->has('foo')); - $this->assertEquals('bar', $session->get('foo')); - $this->assertEquals(0, $session->get('bar')); - - $session->ageFlashData(); - - $this->assertFalse($session->has('foo')); - $this->assertNull($session->get('foo')); - } - - - public function testDataMergeNewFlashes() - { - $session = $this->getSession(); - $session->flash('foo', 'bar'); - $session->set('fu', 'baz'); - $session->set('flash.old', ['qu']); - $this->assertNotFalse(array_search('foo', $session->get('flash.new'))); - $this->assertFalse(array_search('fu', $session->get('flash.new'))); - $session->keep(['fu','qu']); - $this->assertNotFalse(array_search('foo', $session->get('flash.new'))); - $this->assertNotFalse(array_search('fu', $session->get('flash.new'))); - $this->assertNotFalse(array_search('qu', $session->get('flash.new'))); - $this->assertFalse(array_search('qu', $session->get('flash.old'))); - } - - - public function testReflash() - { - $session = $this->getSession(); - $session->flash('foo', 'bar'); - $session->set('flash.old', ['foo']); - $session->reflash(); - $this->assertNotFalse(array_search('foo', $session->get('flash.new'))); - $this->assertFalse(array_search('foo', $session->get('flash.old'))); - } - - - public function testReplace() - { - $session = $this->getSession(); - $session->set('foo', 'bar'); - $session->set('qu', 'ux'); - $session->replace(['foo' => 'baz']); - $this->assertEquals('baz', $session->get('foo')); - $this->assertEquals('ux', $session->get('qu')); - } - - - public function testRemove() - { - $session = $this->getSession(); - $session->set('foo', 'bar'); - $pulled = $session->remove('foo'); - $this->assertFalse($session->has('foo')); - $this->assertEquals('bar', $pulled); - } - - - public function testFlush() - { - $session = $this->getSession(); - $session->put('foo', 'bar'); - - $session->flush(); - - $this->assertFalse($session->has('foo')); - $this->assertEmpty($session->all()); - } - - - public function testHasOldInputWithoutKey() - { - $session = $this->getSession(); - $session->flash('boom', 'baz'); - $this->assertFalse($session->hasOldInput()); - - $session->flashInput(['foo' => 'bar']); - $this->assertTrue($session->hasOldInput()); - } - - - public function testHandlerNeedsRequest() - { - $session = $this->getSession(); - $this->assertFalse($session->handlerNeedsRequest()); - $session->getHandler()->shouldReceive('setRequest')->never(); - - $session = new Store('test', m::mock(new CookieSessionHandler(new CookieJar(), 60))); - $this->assertTrue($session->handlerNeedsRequest()); - $session->getHandler()->shouldReceive('setRequest')->once(); - $request = new Request(); - $session->setRequestOnHandler($request); - } - - - public function testToken() - { - $session = $this->getSession(); - $this->assertEquals($session->token(), $session->getToken()); - } - - - public function testRegenerateToken() - { - $session = $this->getSession(); - $token = $session->getToken(); - $session->regenerateToken(); - $this->assertNotEquals($token, $session->getToken()); - } - - - public function testName() - { - $session = $this->getSession(); - $this->assertEquals($session->getName(), $this->getSessionName()); - $session->setName('foo'); - $this->assertEquals($session->getName(), 'foo'); - } - - - public function testSetPreviousUrl() - { - $session = $this->getSession(); - $session->setPreviousUrl('https://example.com/foo/bar'); - - $this->assertTrue($session->has('_previous.url')); - $this->assertSame('https://example.com/foo/bar', $session->get('_previous.url')); - - $url = $session->previousUrl(); - $this->assertSame('https://example.com/foo/bar', $url); - } - - - public function getSession() - { - $reflection = new ReflectionClass(Store::class); - return $reflection->newInstanceArgs($this->getMocks()); - } - - - public function getMocks() - { - return [ - $this->getSessionName(), - m::mock('SessionHandlerInterface'), - $this->getSessionId(), - ]; - } - - - public function getSessionId() - { - return 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; - } - - - public function getSessionName() - { - return 'name'; - } - -} diff --git a/tests/Support/SupportFluentTest.php b/tests/Support/SupportFluentTest.php deleted file mode 100755 index df0039fc..00000000 --- a/tests/Support/SupportFluentTest.php +++ /dev/null @@ -1,122 +0,0 @@ - 'Taylor', 'age' => 25]; - $fluent = new Fluent($array); - - $refl = new \ReflectionObject($fluent); - $attributes = $refl->getProperty('attributes'); - $attributes->setAccessible(true); - - $this->assertEquals($array, $attributes->getValue($fluent)); - $this->assertEquals($array, $fluent->getAttributes()); - } - - - public function testAttributesAreSetByConstructorGivenStdClass(): void - { - $array = ['name' => 'Taylor', 'age' => 25]; - $fluent = new Fluent((object) $array); - - $refl = new \ReflectionObject($fluent); - $attributes = $refl->getProperty('attributes'); - $attributes->setAccessible(true); - - $this->assertEquals($array, $attributes->getValue($fluent)); - $this->assertEquals($array, $fluent->getAttributes()); - } - - - public function testAttributesAreSetByConstructorGivenArrayIterator(): void - { - $array = ['name' => 'Taylor', 'age' => 25]; - $fluent = new Fluent(new FluentArrayIteratorStub($array)); - - $refl = new \ReflectionObject($fluent); - $attributes = $refl->getProperty('attributes'); - $attributes->setAccessible(true); - - $this->assertEquals($array, $attributes->getValue($fluent)); - $this->assertEquals($array, $fluent->getAttributes()); - } - - - public function testGetMethodReturnsAttribute(): void - { - $fluent = new Fluent(['name' => 'Taylor']); - - $this->assertEquals('Taylor', $fluent->get('name')); - $this->assertEquals('Default', $fluent->get('foo', 'Default')); - $this->assertEquals('Taylor', $fluent->name); - $this->assertNull($fluent->foo); - } - - - public function testMagicMethodsCanBeUsedToSetAttributes(): void - { - $fluent = new Fluent; - - $fluent->name = 'Taylor'; - $fluent->developer(); - $fluent->age(25); - - $this->assertEquals('Taylor', $fluent->name); - $this->assertTrue($fluent->developer); - $this->assertEquals(25, $fluent->age); - $this->assertInstanceOf(Fluent::class, $fluent->programmer()); - } - - - public function testIssetMagicMethod(): void - { - $array = ['name' => 'Taylor', 'age' => 25]; - $fluent = new Fluent($array); - - $this->assertTrue(isset($fluent->name)); - - unset($fluent->name); - - $this->assertFalse(isset($fluent->name)); - } - - - public function testToArrayReturnsAttribute(): void - { - $array = ['name' => 'Taylor', 'age' => 25]; - $fluent = new Fluent($array); - - $this->assertEquals($array, $fluent->toArray()); - } - - - public function testToJsonEncodesTheToArrayResult(): void - { - $fluent = $this->getMock(Fluent::class, ['toArray']); - $fluent->expects($this->once())->method('toArray')->willReturn('foo'); - $results = $fluent->toJson(); - - $this->assertEquals(json_encode('foo'), $results); - } - -} - - -class FluentArrayIteratorStub implements \IteratorAggregate { - protected array $items = []; - - public function __construct(array $items = []) - { - $this->items = (array) $items; - } - - public function getIterator(): Traversable - { - return new \ArrayIterator($this->items); - } -} diff --git a/tests/Support/SupportPluralizerTest.php b/tests/Support/SupportPluralizerTest.php deleted file mode 100755 index 8ef438f5..00000000 --- a/tests/Support/SupportPluralizerTest.php +++ /dev/null @@ -1,53 +0,0 @@ -assertEquals('children', str_plural('child')); - $this->assertEquals('tests', str_plural('test')); - $this->assertEquals('deer', str_plural('deer')); - $this->assertEquals('child', str_singular('children')); - $this->assertEquals('test', str_singular('tests')); - $this->assertEquals('deer', str_singular('deer')); - $this->assertEquals('criterion', str_singular('criteria')); - } - - - public function testCaseSensitiveUsage() - { - $this->assertEquals('Children', str_plural('Child')); - $this->assertEquals('CHILDREN', str_plural('CHILD')); - $this->assertEquals('Tests', str_plural('Test')); - $this->assertEquals('TESTS', str_plural('TEST')); - $this->assertEquals('tests', str_plural('test')); - $this->assertEquals('Deer', str_plural('Deer')); - $this->assertEquals('DEER', str_plural('DEER')); - $this->assertEquals('Child', str_singular('Children')); - $this->assertEquals('CHILD', str_singular('CHILDREN')); - $this->assertEquals('Test', str_singular('Tests')); - $this->assertEquals('TEST', str_singular('TESTS')); - $this->assertEquals('Deer', str_singular('Deer')); - $this->assertEquals('DEER', str_singular('DEER')); - $this->assertEquals('Criterion', str_singular('Criteria')); - $this->assertEquals('CRITERION', str_singular('CRITERIA')); - } - - public function testIfEndOfWord() - { - $this->assertEquals('VortexFields', str_plural('VortexField')); - $this->assertEquals('MatrixFields', str_plural('MatrixField')); - $this->assertEquals('IndexFields', str_plural('IndexField')); - $this->assertEquals('VertexFields', str_plural('VertexField')); - } - - public function testAlreadyPluralizedIrregularWords() - { - $this->assertEquals('children', str_plural('children')); - $this->assertEquals('radii', str_plural('radii')); - $this->assertEquals('teeth', str_plural('teeth')); - } - -} diff --git a/tests/Support/SupportServiceProviderTest.php b/tests/Support/SupportServiceProviderTest.php deleted file mode 100755 index e1436599..00000000 --- a/tests/Support/SupportServiceProviderTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertEquals(realpath(__DIR__ . '/'), $superProvider->guessPackagePath()); - - $superSuperProvider = new SuperSuperProvider(null); - $this->assertEquals(realpath(__DIR__.'/'), $superSuperProvider->guessPackagePath()); - } - -} diff --git a/tests/Support/SupportStrTest.php b/tests/Support/SupportStrTest.php deleted file mode 100755 index 5dcdef2f..00000000 --- a/tests/Support/SupportStrTest.php +++ /dev/null @@ -1,199 +0,0 @@ -assertEquals('Taylor...', Str::words('Taylor Otwell', 1)); - $this->assertEquals('Taylor___', Str::words('Taylor Otwell', 1, '___')); - $this->assertEquals('Taylor Otwell', Str::words('Taylor Otwell', 3)); - } - - - public function testStringTrimmedOnlyWhereNecessary(): void - { - $this->assertEquals(' Taylor Otwell ', Str::words(' Taylor Otwell ', 3)); - $this->assertEquals(' Taylor...', Str::words(' Taylor Otwell ', 1)); - } - - - public function testStringTitle(): void - { - $this->assertEquals('Jefferson Costella', Str::title('jefferson costella')); - $this->assertEquals('Jefferson Costella', Str::title('jefFErson coSTella')); - $this->assertEquals('Admin_Role', Str::title('admin_role')); - $this->assertEquals('', Str::title(null)); - $this->assertEquals('', Str::title('')); - } - - - public function testStringWithoutWordsDoesntProduceError(): void - { - $nbsp = chr(0xC2).chr(0xA0); - $this->assertEquals(' ', Str::words(' ')); - $this->assertEquals($nbsp, Str::words($nbsp)); - } - - - public function testStartsWith(): void - { - $this->assertTrue(Str::startsWith('jason', 'jas')); - $this->assertTrue(Str::startsWith('jason', 'jason')); - $this->assertTrue(Str::startsWith('jason', ['jas'])); - $this->assertFalse(Str::startsWith('jason', 'day')); - $this->assertFalse(Str::startsWith('jason', ['day'])); - $this->assertFalse(Str::startsWith('jason', '')); - } - - public function testEquals(): void - { - self::assertTrue(Str::equals('1234', '1234')); - self::assertTrue(Str::equals('Laravel', 'Laravel')); - self::assertTrue(Str::equals('Laravel', 'laRaVeL')); - self::assertFalse(Str::equals('Laravel', 'laRaVeL', true)); - self::assertTrue(Str::equals('', '')); - self::assertTrue(Str::equals('', null)); - self::assertTrue(Str::equals()); - self::assertFalse(Str::equals(null, 'Laravel')); - self::assertFalse(Str::equals('Laravel')); - self::assertFalse(Str::equals('Laravel', null, true)); - } - - - public function testEndsWith(): void - { - $this->assertTrue(Str::endsWith('jason', 'on')); - $this->assertTrue(Str::endsWith('jason', 'jason')); - $this->assertTrue(Str::endsWith('jason', ['on'])); - $this->assertFalse(Str::endsWith('jason', 'no')); - $this->assertFalse(Str::endsWith('jason', ['no'])); - $this->assertFalse(Str::endsWith('jason', '')); - $this->assertFalse(Str::endsWith('7', ' 7')); - } - - - public function testStrContains(): void - { - $this->assertTrue(Str::contains('taylor', 'ylo')); - $this->assertTrue(Str::contains('taylor', ['ylo'])); - $this->assertFalse(Str::contains('taylor', 'xxx')); - $this->assertFalse(Str::contains('taylor', ['xxx'])); - $this->assertFalse(Str::contains('taylor', '')); - $this->assertFalse(Str::contains('taylor', null)); - $this->assertFalse(Str::contains('', 'y')); - $this->assertFalse(Str::contains(null, 'y')); - } - - - public function testParseCallback(): void - { - $this->assertEquals(['Class', 'method'], Str::parseCallback('Class@method', 'foo')); - $this->assertEquals(['Class', 'foo'], Str::parseCallback('Class', 'foo')); - } - - - public function testSlug(): void - { - $this->assertEquals('hello-world', Str::slug('hello world')); - $this->assertEquals('hello-world', Str::slug('hello-world')); - $this->assertEquals('hello-world', Str::slug('hello_world')); - $this->assertEquals('hello_world', Str::slug('hello_world', '_')); - } - - - public function testFinish(): void - { - $this->assertEquals('abbc', Str::finish('ab', 'bc')); - $this->assertEquals('abbc', Str::finish('abbcbc', 'bc')); - $this->assertEquals('abcbbc', Str::finish('abcbbcbc', 'bc')); - } - - - public function testIs(): void - { - $this->assertTrue(Str::is('/', '/')); - $this->assertFalse(Str::is('/', ' /')); - $this->assertFalse(Str::is('/', '/a')); - $this->assertTrue(Str::is('foo/*', 'foo/bar/baz')); - $this->assertTrue(Str::is('*/foo', 'blah/baz/foo')); - $this->assertFalse(Str::is('*/foo', '')); - $this->assertFalse(Str::is('*/foo', null)); - } - - - public function testLower(): void - { - $this->assertEquals('foo bar baz', Str::lower('FOO BAR BAZ')); - $this->assertEquals('foo bar baz', Str::lower('fOo Bar bAz')); - $this->assertEquals('', Str::lower(null)); - } - - - public function testUpper(): void - { - $this->assertEquals('FOO BAR BAZ', Str::upper('foo bar baz')); - $this->assertEquals('FOO BAR BAZ', Str::upper('foO bAr BaZ')); - $this->assertEquals('', Str::upper(null)); - } - - - public function testLimit(): void - { - $this->assertEquals('Laravel is...', Str::limit('Laravel is a free, open source PHP web application framework.', 10)); - $this->assertEquals('', Str::limit(null)); - $this->assertEquals('', Str::limit('')); - } - - - public function testLength(): void - { - $this->assertEquals(11, Str::length('foo bar baz')); - $this->assertEquals(0, Str::length('')); - $this->assertEquals(0, Str::length(null)); - } - - - public function testQuickRandom(): void - { - $randomInteger = mt_rand(1, 100); - $this->assertEquals($randomInteger, strlen(Str::quickRandom($randomInteger))); - $this->assertIsString(Str::quickRandom()); - $this->assertEquals(16, strlen(Str::quickRandom())); - } - - - public function testRandom(): void - { - $this->assertEquals(16, strlen(Str::random())); - $randomInteger = mt_rand(1, 100); - $this->assertEquals($randomInteger, strlen(Str::random($randomInteger))); - $this->assertIsString(Str::random()); - } - - public function testNumberFormat(): void - { - $this->assertEquals('1,000,000', Str::numberFormat(1000000)); - $this->assertEquals('150.000,00', Str::numberFormat(150000, 2, ',', '.')); - $this->assertEquals('0', Str::numberFormat()); - $this->assertEquals('0', Str::numberFormat(null)); - } - - public function testReplace(): void - { - $this->assertSame('foo bar laravel', Str::replace('baz', 'laravel', 'foo bar baz')); - $this->assertSame('foo bar baz 8.x', Str::replace('?', '8.x', 'foo bar baz ?')); - $this->assertSame('foo/bar/baz', Str::replace(' ', '/', 'foo bar baz')); - $this->assertSame('foo bar baz', Str::replace(['?1', '?2', '?3'], ['foo', 'bar', 'baz'], '?1 ?2 ?3')); - $this->assertEquals('', Str::replace('Yo', 'Laravel', '')); - $this->assertEquals('', Str::replace('Yo', 'Laravel', null)); - } -} From 4b216d1036a808ac30be189ca41909474e1feddc Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 17:42:12 +0700 Subject: [PATCH 10/35] feat(console): add Application::starting() BC shim (task 4.1, SCC-1 remainder) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v13 Support\ServiceProvider::commands() calls Illuminate\Console\Application::starting() to defer command registration, but console is not swapped yet (still fork). App boot fataled ("undefined method Console\Application::starting()") when a provider registered commands. Add a static starting() that collects callbacks and fire them in make() with the console instance (which already has resolveCommands()). Transitional — remove when console swaps to v13 (task 4.2/4.3). Fork suite green (763). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Console/Application.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Illuminate/Console/Application.php b/src/Illuminate/Console/Application.php index 23cc4cd0..ba045a19 100755 --- a/src/Illuminate/Console/Application.php +++ b/src/Illuminate/Console/Application.php @@ -22,6 +22,27 @@ class Application extends \Symfony\Component\Console\Application { */ protected $laravel; + /** + * Callbacks to run when a console application is starting. + * + * ponytail: BC shim for v13 Support\ServiceProvider::commands(), which calls + * Illuminate\Console\Application::starting(). Remove when console swaps to v13 (task 4.2/4.3). + * + * @var callable[] + */ + protected static $startingCallbacks = array(); + + /** + * Register a callback to run when the console application is starting. + * + * @param callable $callback + * @return void + */ + public static function starting($callback) + { + static::$startingCallbacks[] = $callback; + } + /** * Create and boot a new Console application. * @@ -51,6 +72,11 @@ public static function make($app) $app->instance('artisan', $console); + foreach (static::$startingCallbacks as $callback) + { + $callback($console); + } + return $console; } From 5106e7f9c9a0ba8cc5e6a41d2ecb270cc9d87305 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 18:31:45 +0700 Subject: [PATCH 11/35] feat(console): make resolveCommands() resilient to unloadable v13 commands (task 4.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v13 session/cache/etc. register console commands that extend v13 console base classes (MigrationGeneratorCommand, …) absent from the not-yet-swapped fork console, fataling artisan boot. Skip commands that fail to resolve (log + continue) so artisan boots; commands that load fine (migrate, queue, app) still register. Transitional — the skipped generator commands return with the console swap (task 4.2). Fork suite green. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Console/Application.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Console/Application.php b/src/Illuminate/Console/Application.php index ba045a19..a0cd590f 100755 --- a/src/Illuminate/Console/Application.php +++ b/src/Illuminate/Console/Application.php @@ -179,7 +179,19 @@ public function resolveCommands($commands) foreach ($commands as $command) { - $this->resolve($command); + try + { + $this->resolve($command); + } + catch (\Throwable $e) + { + // ponytail: some v13 components (session/cache/…) register console commands + // that extend v13 console base classes absent from the not-yet-swapped fork + // console. Skip the ones that can't load so artisan still boots; they return + // with the console swap (task 4.2). Commands that load fine still register. + error_log('[l13] skipped unresolvable console command ' + . (is_string($command) ? $command : gettype($command)) . ': ' . $e->getMessage()); + } } } From c3f03eeebf44d40a97b9a3fe87b1c41d02299e0e Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 18:49:47 +0700 Subject: [PATCH 12/35] feat(console): shim v13 MigrationGeneratorCommand + Prohibitable so artisan boots (task 4.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit console:^13 can't be swapped yet (hard-requires view:^13 = a large Blade migration, task 4.3). But v13 session/cache/database ship console commands that extend v13 console classes absent from the fork console, fataling artisan boot at class-declaration: - Illuminate\Console\MigrationGeneratorCommand (make:session-table, make:cache-table) - Illuminate\Console\Prohibitable trait (migrate:fresh/refresh/reset) Add minimal fork-console shims so those commands load and artisan boots (unblocking the deploy pipeline: composer install -> artisan clear-compiled/optimize). Generation itself is out of scope until the console swap; combined with the resilient resolveCommands(), unloadable/unnamed commands are skipped. Transitional — remove at the console swap (task 4.2). Fork suite green. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Console/MigrationGeneratorCommand.php | 39 ++++++++++++++++ src/Illuminate/Console/Prohibitable.php | 45 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/Illuminate/Console/MigrationGeneratorCommand.php create mode 100644 src/Illuminate/Console/Prohibitable.php diff --git a/src/Illuminate/Console/MigrationGeneratorCommand.php b/src/Illuminate/Console/MigrationGeneratorCommand.php new file mode 100644 index 00000000..187c1186 --- /dev/null +++ b/src/Illuminate/Console/MigrationGeneratorCommand.php @@ -0,0 +1,39 @@ +error('make:*-table generators require the v13 console (L13 migration task 4.2).'); + } + + return 1; + } +} diff --git a/src/Illuminate/Console/Prohibitable.php b/src/Illuminate/Console/Prohibitable.php new file mode 100644 index 00000000..e706dbe6 --- /dev/null +++ b/src/Illuminate/Console/Prohibitable.php @@ -0,0 +1,45 @@ +components->error('This command is prohibited from running in this environment.'); + } + + return true; + } +} From 539c696d7419ea006e58197c07c5dd55de54152a Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 18:56:00 +0700 Subject: [PATCH 13/35] feat(console): add Command::configureDefaults() shim so v13 migration commands load (task 4.1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v13 database/session/cache console commands declare `#[\Override] configureDefaults()` against the v13 console Command; the fork console Command lacks it, so the Override attribute is an uncatchable compile fatal at class declaration (not skippable via resolveCommands). Add a no-op configureDefaults() so those classes load and artisan boots. The fork configures commands via its constructor instead. Transitional — remove at the console swap (task 4.2). Fork suite green. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Console/Command.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Illuminate/Console/Command.php b/src/Illuminate/Console/Command.php index c81337b4..3809075c 100755 --- a/src/Illuminate/Console/Command.php +++ b/src/Illuminate/Console/Command.php @@ -63,6 +63,19 @@ public function __construct() $this->specifyParameters(); } + /** + * ponytail: BC shim — exists so v13 component commands that declare + * `#[\Override] configureDefaults()` (against the v13 console Command) can load under + * the not-yet-swapped fork console. The fork configures via the constructor / + * specifyParameters() instead, so this is a no-op. Remove at the console swap (task 4.2). + * + * @return void + */ + protected function configureDefaults() + { + // + } + /** * Specify the arguments and options on the command. * From 46f0fb922197738d26d54d57c4000efae9ba7996 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 19:01:30 +0700 Subject: [PATCH 14/35] feat(console): alias Contracts\Console\Kernel to artisan for the v13 Artisan facade (task 4.1) v13's Support\Facades\Artisan resolves Illuminate\Contracts\Console\Kernel, unbound in the fork (no console Kernel yet, task 4.2). Alias it to the console Application in make() so the facade resolves and artisan boots. Transitional. Fork suite green. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Console/Application.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Illuminate/Console/Application.php b/src/Illuminate/Console/Application.php index a0cd590f..ddb39129 100755 --- a/src/Illuminate/Console/Application.php +++ b/src/Illuminate/Console/Application.php @@ -72,6 +72,11 @@ public static function make($app) $app->instance('artisan', $console); + // ponytail: v13's Artisan facade resolves Illuminate\Contracts\Console\Kernel, which + // the fork (no console Kernel yet, task 4.2) doesn't bind. Point it at this console + // application so the facade works. Remove at the console swap. + $app->alias('artisan', 'Illuminate\Contracts\Console\Kernel'); + foreach (static::$startingCallbacks as $callback) { $callback($console); From 50ed6fba3f9bd092572f2f8e6e3e367292f095a8 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 19:09:11 +0700 Subject: [PATCH 15/35] feat(foundation): fix redis alias + add v13 contract aliases for SCC-1 components (task 4.1) registerCoreContainerAliases mapped 'redis' to the renamed-away Illuminate\Redis\Database (v13 = RedisManager) and had no contract aliases for the swapped SCC-1 components, so v13 code type-hinting Contracts\{Events\Dispatcher,Redis\Factory,Cache\Factory,Cache\Repository, Config\Repository} or Database\ConnectionResolverInterface failed to resolve. Fix the redis alias and add the contract aliases. Fork suite green. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Foundation/Application.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 0e1cffd8..b11d17b8 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -1184,7 +1184,7 @@ public function registerCoreContainerAliases() 'auth.reminder' => 'Illuminate\Auth\Reminders\PasswordBroker', 'queue' => 'Illuminate\Queue\QueueManager', 'redirect' => 'Illuminate\Routing\Redirector', - 'redis' => 'Illuminate\Redis\Database', + 'redis' => 'Illuminate\Redis\RedisManager', 'request' => 'Illuminate\Http\Request', 'router' => 'Illuminate\Routing\Router', 'session' => 'Illuminate\Session\SessionManager', @@ -1209,6 +1209,16 @@ public function registerCoreContainerAliases() // BC: Hashing\HasherInterface → Contracts\Hashing\Hasher (task 2.11); keep old name resolvable. // class_alias covers use/typehint/instanceof; make()/autowiring by the old name needs this. $this->alias('hash', 'Illuminate\Hashing\HasherInterface'); + + // L13 SCC-1 swap (task 4.1): the swapped components ship v13 contracts. Alias them to + // the core bindings so v13 code that type-hints the contracts resolves (the v13 + // providers don't always register these against the fork's core aliases). + $this->alias('events', 'Illuminate\Contracts\Events\Dispatcher'); + $this->alias('redis', 'Illuminate\Contracts\Redis\Factory'); + $this->alias('cache', 'Illuminate\Contracts\Cache\Factory'); + $this->alias('cache.store', 'Illuminate\Contracts\Cache\Repository'); + $this->alias('config', 'Illuminate\Contracts\Config\Repository'); + $this->alias('db', 'Illuminate\Database\ConnectionResolverInterface'); } } From 829615834c3ac882f1efa2b8039b3f4e8de0b856 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 19:16:19 +0700 Subject: [PATCH 16/35] fix(auth): AuthManager $this->app -> $this->container for v13 Support\Manager (task 4.1) v13 Support\Manager renamed the base property $app -> $container. The fork AuthManager (Auth not swapped) still referenced $this->app -> 'Undefined property' at resolution. Sweep to $this->container (all uses are container ops v13 supports). Fork suite green. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Auth/AuthManager.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Illuminate/Auth/AuthManager.php b/src/Illuminate/Auth/AuthManager.php index 63798eb3..8e331392 100755 --- a/src/Illuminate/Auth/AuthManager.php +++ b/src/Illuminate/Auth/AuthManager.php @@ -18,11 +18,11 @@ protected function createDriver($driver) // 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. - $guard->setCookieJar($this->app['cookie']); + $guard->setCookieJar($this->container['cookie']); - $guard->setDispatcher($this->app['events']); + $guard->setDispatcher($this->container['events']); - return $guard->setRequest($this->app->refresh('request', $guard, 'setRequest')); + return $guard->setRequest($this->container->refresh('request', $guard, 'setRequest')); } /** @@ -38,7 +38,7 @@ protected function callCustomCreator($driver) if ($custom instanceof Guard) return $custom; - return new Guard($custom, $this->app['session.store']); + return new Guard($custom, $this->container['session.store']); } /** @@ -50,7 +50,7 @@ public function createDatabaseDriver() { $provider = $this->createDatabaseProvider(); - return new Guard($provider, $this->app['session.store']); + return new Guard($provider, $this->container['session.store']); } /** @@ -60,14 +60,14 @@ public function createDatabaseDriver() */ protected function createDatabaseProvider() { - $connection = $this->app['db']->connection(); + $connection = $this->container['db']->connection(); // When using the basic database user provider, we need to inject the table we // want to use, since this is not an Eloquent model we will have no way to // know without telling the provider, so we'll inject the config value. - $table = $this->app['config']['auth.table']; + $table = $this->container['config']['auth.table']; - return new DatabaseUserProvider($connection, $this->app['hash'], $table); + return new DatabaseUserProvider($connection, $this->container['hash'], $table); } /** @@ -79,7 +79,7 @@ public function createEloquentDriver() { $provider = $this->createEloquentProvider(); - return new Guard($provider, $this->app['session.store']); + return new Guard($provider, $this->container['session.store']); } /** @@ -89,9 +89,9 @@ public function createEloquentDriver() */ protected function createEloquentProvider() { - $model = $this->app['config']['auth.model']; + $model = $this->container['config']['auth.model']; - return new EloquentUserProvider($this->app['hash'], $model); + return new EloquentUserProvider($this->container['hash'], $model); } /** @@ -101,7 +101,7 @@ protected function createEloquentProvider() */ public function getDefaultDriver() { - return $this->app['config']['auth.driver']; + return $this->container['config']['auth.driver']; } /** @@ -112,7 +112,7 @@ public function getDefaultDriver() */ public function setDefaultDriver($name) { - $this->app['config']['auth.driver'] = $name; + $this->container['config']['auth.driver'] = $name; } } From e96ecc487c28c057860cb6c3b54dd3dfe88a1164 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 19:34:39 +0700 Subject: [PATCH 17/35] fix(console): alias Contracts\Console\Kernel to 'artisan' globally (task 4.1) The v13 Artisan facade resolves Illuminate\Contracts\Console\Kernel. The earlier alias lived only in Console\Application::make() (the php-artisan path), so Artisan::call() in integration tests (and web) still failed. 'artisan' is a lazy singleton, so alias the contract to it in registerCoreContainerAliases (runs in all contexts). Fork suite green. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Console/Application.php | 5 ----- src/Illuminate/Foundation/Application.php | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Illuminate/Console/Application.php b/src/Illuminate/Console/Application.php index ddb39129..a0cd590f 100755 --- a/src/Illuminate/Console/Application.php +++ b/src/Illuminate/Console/Application.php @@ -72,11 +72,6 @@ public static function make($app) $app->instance('artisan', $console); - // ponytail: v13's Artisan facade resolves Illuminate\Contracts\Console\Kernel, which - // the fork (no console Kernel yet, task 4.2) doesn't bind. Point it at this console - // application so the facade works. Remove at the console swap. - $app->alias('artisan', 'Illuminate\Contracts\Console\Kernel'); - foreach (static::$startingCallbacks as $callback) { $callback($console); diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index b11d17b8..4469f24f 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -1219,6 +1219,12 @@ public function registerCoreContainerAliases() $this->alias('cache.store', 'Illuminate\Contracts\Cache\Repository'); $this->alias('config', 'Illuminate\Contracts\Config\Repository'); $this->alias('db', 'Illuminate\Database\ConnectionResolverInterface'); + + // ponytail: v13's Support\Facades\Artisan resolves Illuminate\Contracts\Console\Kernel. + // The fork has no console Kernel (task 4.2); 'artisan' is a lazy singleton + // (ArtisanServiceProvider), so alias the contract to it globally — works for the + // Artisan facade in web/console/tests (make()-only aliasing missed Artisan::call()). + $this->alias('artisan', 'Illuminate\Contracts\Console\Kernel'); } } From b96117e8fff68869311b980c72adcb89ffc35291 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 21:14:33 +0700 Subject: [PATCH 18/35] fix(foundation): guard deferred-provider boot() like the eager loop (task 4.1) registerDeferredProvider() called $instance->boot() unconditionally in the booting callback; v13 ServiceProvider has no default boot() (e.g. deferred CacheServiceProvider) -> 'undefined method boot()'. Guard with method_exists + call via the container, matching the eager boot() loop fix. Fork suite green. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Foundation/Application.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 4469f24f..85f052b7 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -441,7 +441,12 @@ public function registerDeferredProvider($provider, $service = null) { $this->booting(function() use ($instance) { - $instance->boot(); + // v13 ServiceProvider has no default boot(); call only when defined + // (mirrors the eager boot() loop). Via the container so boot() DI works. + if (method_exists($instance, 'boot')) + { + $this->call([$instance, 'boot']); + } }); } } From 7b26384bdf90e793787747bedf688cea06dd5922 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 22:57:16 +0700 Subject: [PATCH 19/35] fix(mail): alias 'mail.manager' to fork mailer for v13 Mail facade (task 4.1) v13 illuminate/support Facades\Mail resolves 'mail.manager', but the fork Mail component is unswapped and binds 'mailer'. Alias the two (and expose 'mail.manager' in provides()) so Mail::send() works after the SCC-1 support swap. Remove when Mail swaps to illuminate/mail:^13. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Mail/MailServiceProvider.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Mail/MailServiceProvider.php b/src/Illuminate/Mail/MailServiceProvider.php index 396fab30..7ffda452 100755 --- a/src/Illuminate/Mail/MailServiceProvider.php +++ b/src/Illuminate/Mail/MailServiceProvider.php @@ -59,6 +59,10 @@ public function register(): void return $mailer; }); + + // ponytail: v13 Support\Facades\Mail resolves 'mail.manager'; fork Mail is unswapped and + // binds 'mailer'. Alias so Mail::send() works. Remove when Mail swaps to illuminate/mail:^13. + $this->app->alias('mailer', 'mail.manager'); } /** @@ -244,7 +248,7 @@ protected function registerLogTransport(array $config): void #[\Override] public function provides(): array { - return ['mailer', 'symfony.transport']; + return ['mailer', 'mail.manager', 'symfony.transport']; } } From c294c49dbd14a9c118d9775ad6797a794cb8e6f8 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 23:46:05 +0700 Subject: [PATCH 20/35] feat(pagination): swap illuminate/pagination to ^13 (task 4.1 tail) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v13 illuminate/database paginate()/simplePaginate() call Illuminate\Pagination\Paginator::resolveCurrentPage() and instantiate LengthAwarePaginator — classes absent from the L4.2 fork pagination tree (only Paginator/Factory/Presenter existed), causing 86 integration errors. Pagination was missed by the SCC-1 atomic swap. - composer: illuminate/pagination self.version(replace) -> ^13 (require) - delete the shadowed fork src/Illuminate/Pagination tree - drop the 'paginator' => Pagination\Factory core alias (no v13 equivalent; v13 uses static Paginator resolvers, and nothing resolves 'paginator') App consumes v13's PaginationServiceProvider (already registered by FQCN). Co-Authored-By: Claude Opus 4.8 (1M context) --- composer.json | 2 +- src/Illuminate/Foundation/Application.php | 1 - .../Pagination/BootstrapPresenter.php | 42 -- src/Illuminate/Pagination/Factory.php | 289 ---------- .../Pagination/PaginationServiceProvider.php | 44 -- src/Illuminate/Pagination/Paginator.php | 545 ------------------ src/Illuminate/Pagination/Presenter.php | 277 --------- src/Illuminate/Pagination/composer.json | 30 - src/Illuminate/Pagination/views/simple.php | 15 - src/Illuminate/Pagination/views/slider-3.php | 9 - src/Illuminate/Pagination/views/slider.php | 11 - 11 files changed, 1 insertion(+), 1264 deletions(-) delete mode 100644 src/Illuminate/Pagination/BootstrapPresenter.php delete mode 100755 src/Illuminate/Pagination/Factory.php delete mode 100755 src/Illuminate/Pagination/PaginationServiceProvider.php delete mode 100755 src/Illuminate/Pagination/Paginator.php delete mode 100755 src/Illuminate/Pagination/Presenter.php delete mode 100755 src/Illuminate/Pagination/composer.json delete mode 100755 src/Illuminate/Pagination/views/simple.php delete mode 100755 src/Illuminate/Pagination/views/slider-3.php delete mode 100755 src/Illuminate/Pagination/views/slider.php diff --git a/composer.json b/composer.json index cd10dd3d..02102660 100755 --- a/composer.json +++ b/composer.json @@ -26,6 +26,7 @@ "illuminate/filesystem": "^13", "illuminate/http": "^13", "illuminate/macroable": "^13", + "illuminate/pagination": "^13", "illuminate/pipeline": "^13", "illuminate/redis": "^13", "illuminate/reflection": "^13", @@ -64,7 +65,6 @@ "illuminate/html": "self.version", "illuminate/log": "self.version", "illuminate/mail": "self.version", - "illuminate/pagination": "self.version", "illuminate/queue": "self.version", "illuminate/routing": "self.version", "illuminate/translation": "self.version", diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 85f052b7..2b94a316 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -1185,7 +1185,6 @@ public function registerCoreContainerAliases() 'translator' => 'Illuminate\Translation\Translator', 'log' => 'Illuminate\Log\Logger', 'mailer' => 'Illuminate\Mail\Mailer', - 'paginator' => 'Illuminate\Pagination\Factory', 'auth.reminder' => 'Illuminate\Auth\Reminders\PasswordBroker', 'queue' => 'Illuminate\Queue\QueueManager', 'redirect' => 'Illuminate\Routing\Redirector', diff --git a/src/Illuminate/Pagination/BootstrapPresenter.php b/src/Illuminate/Pagination/BootstrapPresenter.php deleted file mode 100644 index dc9c7c24..00000000 --- a/src/Illuminate/Pagination/BootstrapPresenter.php +++ /dev/null @@ -1,42 +0,0 @@ -'.$page.''; - } - - /** - * Get HTML wrapper for disabled text. - * - * @param string $text - * @return string - */ - public function getDisabledTextWrapper($text) - { - return '
  • '.$text.'
  • '; - } - - /** - * Get HTML wrapper for active text. - * - * @param string $text - * @return string - */ - public function getActivePageWrapper($text) - { - return '
  • '.$text.'
  • '; - } - -} diff --git a/src/Illuminate/Pagination/Factory.php b/src/Illuminate/Pagination/Factory.php deleted file mode 100755 index 43a70799..00000000 --- a/src/Illuminate/Pagination/Factory.php +++ /dev/null @@ -1,289 +0,0 @@ -view = $view; - $this->trans = $trans; - $this->request = $request; - $this->pageName = $pageName; - $this->setupPaginationEnvironment(); - } - - /** - * Setup the pagination environment. - * - * @return void - */ - protected function setupPaginationEnvironment() - { - $this->view->addNamespace('pagination', __DIR__.'/views'); - } - - /** - * Get a new paginator instance. - * - * @param array $items - * @param int $total - * @param int|null $perPage - * @return \Illuminate\Pagination\Paginator - */ - public function make(array $items, $total, $perPage = null) - { - $paginator = new Paginator($this, $items, $total, $perPage); - - return $paginator->setupPaginationContext(); - } - - /** - * Get the pagination view. - * - * @param \Illuminate\Pagination\Paginator $paginator - * @param string $view - * @return \Illuminate\View\View - */ - public function getPaginationView(Paginator $paginator, $view = null) - { - $data = array('environment' => $this, 'paginator' => $paginator); - - return $this->view->make($this->getViewName($view), $data); - } - - /** - * Get the number of the current page. - * - * @return int - */ - public function getCurrentPage() - { - $page = (int) $this->currentPage ?: $this->request->input($this->pageName, 1); - - if ($page < 1 || filter_var($page, FILTER_VALIDATE_INT) === false) - { - return 1; - } - - return $page; - } - - /** - * Set the number of the current page. - * - * @param int $number - * @return void - */ - public function setCurrentPage($number) - { - $this->currentPage = $number; - } - - /** - * Get the root URL for the request. - * - * @return string - */ - public function getCurrentUrl() - { - return $this->baseUrl ?: $this->request->url(); - } - - /** - * Set the base URL in use by the paginator. - * - * @param string $baseUrl - * @return void - */ - public function setBaseUrl($baseUrl) - { - $this->baseUrl = $baseUrl; - } - - /** - * Set the input page parameter name used by the paginator. - * - * @param string $pageName - * @return void - */ - public function setPageName($pageName) - { - $this->pageName = $pageName; - } - - /** - * Get the input page parameter name used by the paginator. - * - * @return string - */ - public function getPageName() - { - return $this->pageName; - } - - /** - * Get the name of the pagination view. - * - * @param string $view - * @return string - */ - public function getViewName($view = null) - { - if ( ! is_null($view)) return $view; - - return $this->viewName ?: 'pagination::slider'; - } - - /** - * Set the name of the pagination view. - * - * @param string $viewName - * @return void - */ - public function setViewName($viewName) - { - $this->viewName = $viewName; - } - - /** - * Get the locale of the paginator. - * - * @return string - */ - public function getLocale() - { - return $this->locale; - } - - /** - * Set the locale of the paginator. - * - * @param string $locale - * @return void - */ - public function setLocale($locale) - { - $this->locale = $locale; - } - - /** - * Get the active request instance. - * - * @return \Symfony\Component\HttpFoundation\Request - */ - public function getRequest() - { - return $this->request; - } - - /** - * Set the active request instance. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * @return void - */ - public function setRequest(Request $request) - { - $this->request = $request; - } - - /** - * Get the current view factory. - * - * @return \Illuminate\View\Factory - */ - public function getViewFactory() - { - return $this->view; - } - - /** - * Set the current view factory. - * - * @param \Illuminate\View\Factory $view - * @return void - */ - public function setViewFactory(ViewFactory $view) - { - $this->view = $view; - } - - /** - * Get the translator instance. - * - * @return \Symfony\Contracts\Translation\TranslatorInterface - */ - public function getTranslator() - { - return $this->trans; - } - -} diff --git a/src/Illuminate/Pagination/PaginationServiceProvider.php b/src/Illuminate/Pagination/PaginationServiceProvider.php deleted file mode 100755 index 933e82ca..00000000 --- a/src/Illuminate/Pagination/PaginationServiceProvider.php +++ /dev/null @@ -1,44 +0,0 @@ -app->singleton('paginator', function($app) - { - $paginator = new Factory($app['request'], $app['view'], $app['translator']); - - $paginator->setViewName($app['config']['view.pagination']); - - $app->refresh('request', $paginator, 'setRequest'); - - return $paginator; - }); - } - - /** - * Get the services provided by the provider. - * - * @return array - */ - #[\Override] - public function provides() - { - return array('paginator'); - } - -} diff --git a/src/Illuminate/Pagination/Paginator.php b/src/Illuminate/Pagination/Paginator.php deleted file mode 100755 index 121ef27e..00000000 --- a/src/Illuminate/Pagination/Paginator.php +++ /dev/null @@ -1,545 +0,0 @@ -factory = $factory; - - if (is_null($perPage)) - { - $this->perPage = (int) $total; - $this->hasMore = count($items) > $this->perPage; - $this->items = array_slice($items, 0, $this->perPage); - } - else - { - $this->items = $items; - $this->total = (int) $total; - $this->perPage = (int) $perPage; - } - } - - /** - * Setup the pagination context (current and last page). - * - * @return $this - */ - public function setupPaginationContext() - { - $this->calculateCurrentAndLastPages(); - - $this->calculateItemRanges(); - - return $this; - } - - /** - * Calculate the current and last pages for this instance. - * - * @return void - */ - protected function calculateCurrentAndLastPages() - { - if ($this->isQuickPaginating()) - { - $this->currentPage = $this->factory->getCurrentPage(); - - $this->lastPage = $this->hasMore ? $this->currentPage + 1 : $this->currentPage; - } - else - { - $this->lastPage = max((int) ceil($this->total / $this->perPage), 1); - - $this->currentPage = $this->calculateCurrentPage($this->lastPage); - } - } - - /** - * Calculate the first and last item number for this instance. - * - * @return void - */ - protected function calculateItemRanges() - { - $this->from = $this->total ? ($this->currentPage - 1) * $this->perPage + 1 : 0; - - $this->to = min($this->total, $this->currentPage * $this->perPage); - } - - /** - * Get the current page for the request. - * - * @param int $lastPage - * @return int - */ - protected function calculateCurrentPage($lastPage) - { - $page = $this->factory->getCurrentPage(); - - // The page number will get validated and adjusted if it either less than one - // or greater than the last page available based on the count of the given - // items array. If it's greater than the last, we'll give back the last. - if (is_numeric($page) && $page > $lastPage) - { - return $lastPage > 0 ? $lastPage : 1; - } - - return $this->isValidPageNumber($page) ? (int) $page : 1; - } - - /** - * Determine if the given value is a valid page number. - * - * @param int $page - * @return bool - */ - protected function isValidPageNumber($page) - { - return $page >= 1 && filter_var($page, FILTER_VALIDATE_INT) !== false; - } - - /** - * Get the pagination links view. - * - * @param string $view - * @return \Illuminate\View\View - */ - public function links($view = null) - { - return $this->factory->getPaginationView($this, $view); - } - - /** - * Get a URL for a given page number. - * - * @param int $page - * @return string - */ - public function getUrl($page) - { - $parameters = array( - $this->factory->getPageName() => $page, - ); - - // If we have any extra query string key / value pairs that need to be added - // onto the URL, we will put them in query string form and then attach it - // to the URL. This allows for extra information like sortings storage. - if (count($this->query) > 0) - { - $parameters = array_merge($this->query, $parameters); - } - - $fragment = $this->buildFragment(); - - return $this->factory->getCurrentUrl().'?'.http_build_query($parameters, '', '&').$fragment; - } - - /** - * Get / set the URL fragment to be appended to URLs. - * - * @param string|null $fragment - * @return $this|string - */ - public function fragment($fragment = null) - { - if (is_null($fragment)) return $this->fragment; - - $this->fragment = $fragment; - - return $this; - } - - /** - * Build the full fragment portion of a URL. - * - * @return string - */ - protected function buildFragment() - { - return $this->fragment ? '#'.$this->fragment : ''; - } - - /** - * Add a query string value to the paginator. - * - * @param string $key - * @param string $value - * @return $this - */ - public function appends($key, $value = null) - { - if (is_array($key)) return $this->appendArray($key); - - return $this->addQuery($key, $value); - } - - /** - * Add an array of query string values. - * - * @param array $keys - * @return $this - */ - protected function appendArray(array $keys) - { - foreach ($keys as $key => $value) - { - $this->addQuery($key, $value); - } - - return $this; - } - - /** - * Add a query string value to the paginator. - * - * @param string $key - * @param string $value - * @return $this - */ - public function addQuery($key, $value) - { - if ($key !== $this->factory->getPageName()) - { - $this->query[$key] = $value; - } - - return $this; - } - - /** - * Determine if the paginator is doing "quick" pagination. - * - * @return bool - */ - public function isQuickPaginating() - { - return is_null($this->total); - } - - /** - * Get the current page for the request. - * - * @param int|null $total - * @return int - */ - public function currentPage($total = null) - { - if (is_null($total)) - { - return $this->currentPage; - } - - return min($this->currentPage, (int) ceil($total / $this->perPage)); - } - - /** - * Get the last page that should be available. - * - * @return int - */ - public function lastPage() - { - return $this->lastPage; - } - - /** - * Get the number of the first item on the paginator. - * - * @return int - */ - public function firstItem() - { - return $this->from; - } - - /** - * Get the number of the last item on the paginator. - * - * @return int - */ - public function lastItem() - { - return $this->to; - } - - /** - * Get the number of items to be displayed per page. - * - * @return int - */ - public function perPage() - { - return $this->perPage; - } - - /** - * Get a collection instance containing the items. - * - * @return \Illuminate\Support\Collection - */ - public function getCollection() - { - return new Collection($this->items); - } - - /** - * Get the items being paginated. - * - * @return array - */ - public function items() - { - return $this->items; - } - - /** - * Set the items being paginated. - * - * @param mixed $items - * @return void - */ - public function setItems($items) - { - $this->items = $items; - } - - /** - * Get the total number of items in the collection. - * - * @return int - */ - public function total() - { - return $this->total; - } - - /** - * Set the base URL in use by the paginator. - * - * @param string $baseUrl - * @return void - */ - public function setBaseUrl($baseUrl) - { - $this->factory->setBaseUrl($baseUrl); - } - - /** - * Get the pagination factory. - * - * @return \Illuminate\Pagination\Factory - */ - public function getFactory() - { - return $this->factory; - } - - /** - * Get an iterator for the items. - * - * @return \ArrayIterator - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } - - /** - * Determine if the list of items is empty or not. - * - * @return bool - */ - public function isEmpty() - { - return empty($this->items); - } - - /** - * Get the number of items for the current page. - * - * @return int - */ - public function count(): int - { - return count($this->items); - } - - /** - * Determine if the given item exists. - * - * @param mixed $key - * @return bool - */ - public function offsetExists($key): bool - { - return array_key_exists($key, $this->items); - } - - /** - * Get the item at the given offset. - * - * @param mixed $key - * @return mixed - */ - public function offsetGet($key): mixed - { - return $this->items[$key]; - } - - /** - * Set the item at the given offset. - * - * @param mixed $key - * @param mixed $value - * @return void - */ - public function offsetSet($key, $value): void - { - $this->items[$key] = $value; - } - - /** - * Unset the item at the given key. - * - * @param mixed $key - * @return void - */ - public function offsetUnset($key): void - { - unset($this->items[$key]); - } - - /** - * Get the instance as an array. - * - * @return array - */ - public function toArray() - { - return array( - 'total' => $this->total, 'per_page' => $this->perPage, - 'current_page' => $this->currentPage, 'last_page' => $this->lastPage, - 'from' => $this->from, 'to' => $this->to, 'data' => $this->getCollection()->toArray(), - ); - } - - /** - * Convert the object to its JSON representation. - * - * @param int $options - * @return string - */ - public function toJson($options = 0) - { - return json_encode($this->toArray(), $options); - } - - /** - * Call a method on the underlying Collection - * - * @param string $method - * @param array $arguments - * @return mixed - */ - public function __call($method, $arguments) - { - return call_user_func_array(array($this->getCollection(), $method), $arguments); - } - -} diff --git a/src/Illuminate/Pagination/Presenter.php b/src/Illuminate/Pagination/Presenter.php deleted file mode 100755 index 42a9eb78..00000000 --- a/src/Illuminate/Pagination/Presenter.php +++ /dev/null @@ -1,277 +0,0 @@ -paginator = $paginator; - $this->lastPage = $this->paginator->lastPage(); - $this->currentPage = $this->paginator->currentPage(); - } - - /** - * Get HTML wrapper for a page link. - * - * @param string $url - * @param int $page - * @param string $rel - * @return string - */ - abstract public function getPageLinkWrapper($url, $page, $rel = null); - - /** - * Get HTML wrapper for disabled text. - * - * @param string $text - * @return string - */ - abstract public function getDisabledTextWrapper($text); - - /** - * Get HTML wrapper for active text. - * - * @param string $text - * @return string - */ - abstract public function getActivePageWrapper($text); - - /** - * Render the Pagination contents. - * - * @return string - */ - public function render() - { - // The hard-coded thirteen represents the minimum number of pages we need to - // be able to create a sliding page window. If we have less than that, we - // will just render a simple range of page links insteadof the sliding. - if ($this->lastPage < 13) - { - $content = $this->getPageRange(1, $this->lastPage); - } - else - { - $content = $this->getPageSlider(); - } - - return $this->getPrevious().$content.$this->getNext(); - } - - /** - * Create a range of pagination links. - * - * @param int $start - * @param int $end - * @return string - */ - public function getPageRange($start, $end) - { - $pages = array(); - - for ($page = $start; $page <= $end; $page++) - { - // If the current page is equal to the page we're iterating on, we will create a - // disabled link for that page. Otherwise, we can create a typical active one - // for the link. We will use this implementing class's methods to get HTML. - if ($this->currentPage == $page) - { - $pages[] = $this->getActivePageWrapper($page); - } - else - { - $pages[] = $this->getLink($page); - } - } - - return implode('', $pages); - } - - /** - * Create a pagination slider link window. - * - * @return string - */ - protected function getPageSlider() - { - $window = 6; - - // If the current page is very close to the beginning of the page range, we will - // just render the beginning of the page range, followed by the last 2 of the - // links in this list, since we will not have room to create a full slider. - if ($this->currentPage <= $window) - { - $ending = $this->getFinish(); - - return $this->getPageRange(1, $window + 2).$ending; - } - - // If the current page is close to the ending of the page range we will just get - // this first couple pages, followed by a larger window of these ending pages - // since we're too close to the end of the list to create a full on slider. - elseif ($this->currentPage >= $this->lastPage - $window) - { - $start = $this->lastPage - 8; - - $content = $this->getPageRange($start, $this->lastPage); - - return $this->getStart().$content; - } - - // If we have enough room on both sides of the current page to build a slider we - // will surround it with both the beginning and ending caps, with this window - // of pages in the middle providing a Google style sliding paginator setup. - else - { - $content = $this->getAdjacentRange(); - - return $this->getStart().$content.$this->getFinish(); - } - } - - /** - * Get the page range for the current page window. - * - * @return string - */ - public function getAdjacentRange() - { - return $this->getPageRange($this->currentPage - 3, $this->currentPage + 3); - } - - /** - * Create the beginning leader of a pagination slider. - * - * @return string - */ - public function getStart() - { - return $this->getPageRange(1, 2).$this->getDots(); - } - - /** - * Create the ending cap of a pagination slider. - * - * @return string - */ - public function getFinish() - { - $content = $this->getPageRange($this->lastPage - 1, $this->lastPage); - - return $this->getDots().$content; - } - - /** - * Get the previous page pagination element. - * - * @param string $text - * @return string - */ - public function getPrevious($text = '«') - { - // If the current page is less than or equal to one, it means we can't go any - // further back in the pages, so we will render a disabled previous button - // when that is the case. Otherwise, we will give it an active "status". - if ($this->currentPage <= 1) - { - return $this->getDisabledTextWrapper($text); - } - - $url = $this->paginator->getUrl($this->currentPage - 1); - - return $this->getPageLinkWrapper($url, $text, 'prev'); - } - - /** - * Get the next page pagination element. - * - * @param string $text - * @return string - */ - public function getNext($text = '»') - { - // If the current page is greater than or equal to the last page, it means we - // can't go any further into the pages, as we're already on this last page - // that is available, so we will make it the "next" link style disabled. - if ($this->currentPage >= $this->lastPage) - { - return $this->getDisabledTextWrapper($text); - } - - $url = $this->paginator->getUrl($this->currentPage + 1); - - return $this->getPageLinkWrapper($url, $text, 'next'); - } - - /** - * Get a pagination "dot" element. - * - * @return string - */ - public function getDots() - { - return $this->getDisabledTextWrapper("..."); - } - - /** - * Create a pagination slider link. - * - * @param mixed $page - * @return string - */ - public function getLink($page) - { - $url = $this->paginator->getUrl($page); - - return $this->getPageLinkWrapper($url, $page); - } - - /** - * Set the value of the current page. - * - * @param int $page - * @return void - */ - public function setCurrentPage($page) - { - $this->currentPage = $page; - } - - /** - * Set the value of the last page. - * - * @param int $page - * @return void - */ - public function setLastPage($page) - { - $this->lastPage = $page; - } - -} diff --git a/src/Illuminate/Pagination/composer.json b/src/Illuminate/Pagination/composer.json deleted file mode 100755 index 96c959f8..00000000 --- a/src/Illuminate/Pagination/composer.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "illuminate/pagination", - "license": "MIT", - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "require": { - "php": ">=5.4.0", - "illuminate/http": "4.2.*", - "illuminate/support": "4.2.*", - "illuminate/view": "4.2.*", - "symfony/http-foundation": "~6.4", - "symfony/translation": "~6.4" - }, - "autoload": { - "psr-0": { - "Illuminate\\Pagination": "" - } - }, - "target-dir": "Illuminate/Pagination", - "extra": { - "branch-alias": { - "dev-master": "4.2-dev" - } - }, - "minimum-stability": "dev" -} diff --git a/src/Illuminate/Pagination/views/simple.php b/src/Illuminate/Pagination/views/simple.php deleted file mode 100755 index 36353c32..00000000 --- a/src/Illuminate/Pagination/views/simple.php +++ /dev/null @@ -1,15 +0,0 @@ -getTranslator(); -?> - -lastPage() > 1): ?> -
      - getPrevious($trans->trans('pagination.previous')); - - echo $presenter->getNext($trans->trans('pagination.next')); - ?> -
    - diff --git a/src/Illuminate/Pagination/views/slider-3.php b/src/Illuminate/Pagination/views/slider-3.php deleted file mode 100755 index 0131087b..00000000 --- a/src/Illuminate/Pagination/views/slider-3.php +++ /dev/null @@ -1,9 +0,0 @@ - - -lastPage() > 1): ?> -
      - render(); ?> -
    - diff --git a/src/Illuminate/Pagination/views/slider.php b/src/Illuminate/Pagination/views/slider.php deleted file mode 100755 index af10c3c9..00000000 --- a/src/Illuminate/Pagination/views/slider.php +++ /dev/null @@ -1,11 +0,0 @@ - - -lastPage() > 1): ?> - - From 2eb09ce35a86c53ef558989e9ae838145feaecf7 Mon Sep 17 00:00:00 2001 From: agis Date: Fri, 18 Sep 2026 23:55:07 +0700 Subject: [PATCH 21/35] fix(foundation): add resourcePath() for v13 ServiceProviders (task 4.1 tail) v13 PaginationServiceProvider::boot() (and other v13 providers) call $app->resourcePath(); the L4.2 fork Application lacks it -> fatal during console boot after the pagination swap. Minimal shim off path.base. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Foundation/Application.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 2b94a316..b5f25133 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -203,6 +203,20 @@ public function bindInstallPaths(array $paths) } } + /** + * Get the path to the resources directory. + * + * ponytail: v13 ServiceProviders (e.g. PaginationServiceProvider) call resourcePath(); + * the L4.2 fork Application lacks it. Remove once Foundation swaps to v13. + * + * @param string $path + * @return string + */ + public function resourcePath($path = '') + { + return $this['path.base'].DIRECTORY_SEPARATOR.'resources'.($path != '' ? DIRECTORY_SEPARATOR.$path : ''); + } + /** * Get the application bootstrap file. * From 35d1009c770db02ed9a605da88d85d8e026f573a Mon Sep 17 00:00:00 2001 From: agis Date: Sat, 19 Sep 2026 01:33:35 +0700 Subject: [PATCH 22/35] fix(session): csrf_token()/FormBuilder use v13 Store::token() not getToken() v13 illuminate/session Store renamed getToken() -> token(); the fork's csrf_token() helper and Html FormBuilder still called getToken(), fataling in every view that renders a CSRF field (~401 integration failures). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Html/HtmlServiceProvider.php | 2 +- src/Illuminate/Support/helpers.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Html/HtmlServiceProvider.php b/src/Illuminate/Html/HtmlServiceProvider.php index 0c1f2413..7944b486 100755 --- a/src/Illuminate/Html/HtmlServiceProvider.php +++ b/src/Illuminate/Html/HtmlServiceProvider.php @@ -45,7 +45,7 @@ protected function registerFormBuilder() { $this->app->singleton('form', function($app) { - $form = new FormBuilder($app['html'], $app['url'], $app['session.store']->getToken()); + $form = new FormBuilder($app['html'], $app['url'], $app['session.store']->token()); return $form->setSessionStore($app['session.store']); }); diff --git a/src/Illuminate/Support/helpers.php b/src/Illuminate/Support/helpers.php index adc6e3a2..560bc9fe 100755 --- a/src/Illuminate/Support/helpers.php +++ b/src/Illuminate/Support/helpers.php @@ -441,7 +441,7 @@ function csrf_token() if (isset($session)) { - return $session->getToken(); + return $session->token(); } throw new RuntimeException("Application session store not set."); From a8ddcfedccb9392431fd97641bfdf514f515fa74 Mon Sep 17 00:00:00 2001 From: agis Date: Sat, 19 Sep 2026 11:17:07 +0700 Subject: [PATCH 23/35] fix(testing): Client::filterFiles returns Illuminate UploadedFile in test mode v13 Request::convertUploadedFiles() re-wraps each upload via UploadedFile::createFromBase() with test=false, so files posted through the BrowserKit test client fail isValid()/mimes validation. Return Illuminate\Http\UploadedFile instances so createFromBase's instanceof check preserves the test flag. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Foundation/Testing/Client.php | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Illuminate/Foundation/Testing/Client.php b/src/Illuminate/Foundation/Testing/Client.php index e97ea39a..086bf2ff 100755 --- a/src/Illuminate/Foundation/Testing/Client.php +++ b/src/Illuminate/Foundation/Testing/Client.php @@ -21,6 +21,30 @@ protected function filterRequest(DomRequest $request): \Symfony\Component\HttpFo return $httpRequest; } + /** + * v13 Request::convertUploadedFiles() re-wraps each file via UploadedFile::createFromBase() + * with test=false, so a plain Symfony upload fails isValid()/mimes under tests. Handing back + * Illuminate\Http\UploadedFile instances lets that instanceof check preserve the test flag. + */ + #[\Override] + protected function filterFiles(array $files): array + { + return $this->toTestUploadedFiles(parent::filterFiles($files)); + } + + private function toTestUploadedFiles(array $files): array + { + foreach ($files as $key => $file) { + if (is_array($file)) { + $files[$key] = $this->toTestUploadedFiles($file); + } elseif ($file instanceof \Symfony\Component\HttpFoundation\File\UploadedFile) { + $files[$key] = \Illuminate\Http\UploadedFile::createFromBase($file, true); + } + } + + return $files; + } + /** * Get the request parameters from a BrowserKit request. * From 38a6092c11062a5a6130b304e9a9c8791b354a99 Mon Sep 17 00:00:00 2001 From: agis Date: Tue, 22 Sep 2026 14:06:11 +0700 Subject: [PATCH 24/35] feat(view): swap illuminate/view to ^13 (task 4.3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove illuminate/view from replace + require illuminate/view:^13 (pulls pagination v13.32.0 too). v13 view shadows the fork View tree via PSR-4 (Illuminate\View\ > Illuminate\); tree left on disk (evaporates at 4.4). Deleted tests/View + tests/Pagination (vendored-component tests, same policy as tests/Container at 4.1). Fixed SupportUtilTest paginator ctor to v13 signature (items,perPage) — Util is fork-only, test is legit. Suite 602 green. Co-Authored-By: Claude Opus 4.8 (1M context) --- composer.json | 2 +- .../PaginationBootstrapPresenterTest.php | 174 ----- .../PaginationCustomPresenterTest.php | 52 -- tests/Pagination/PaginationFactoryTest.php | 111 ---- tests/Pagination/PaginationPaginatorTest.php | 227 ------- tests/Support/SupportUtilTest.php | 13 +- tests/View/ViewBladeCompilerTest.php | 627 ------------------ tests/View/ViewCompilerEngineTest.php | 50 -- tests/View/ViewEngineResolverTest.php | 24 - tests/View/ViewFactoryTest.php | 378 ----------- tests/View/ViewFinderTest.php | 155 ----- tests/View/ViewPhpEngineTest.php | 22 - tests/View/ViewTest.php | 232 ------- tests/View/fixtures/basic.php | 1 - tests/View/fixtures/namespaced/basic.php | 1 - tests/View/fixtures/nested/basic.php | 1 - tests/View/fixtures/nested/child.php | 1 - .../fixtures/section-exception-layout.php | 1 - tests/View/fixtures/section-exception.php | 4 - 19 files changed, 3 insertions(+), 2073 deletions(-) delete mode 100755 tests/Pagination/PaginationBootstrapPresenterTest.php delete mode 100644 tests/Pagination/PaginationCustomPresenterTest.php delete mode 100755 tests/Pagination/PaginationFactoryTest.php delete mode 100755 tests/Pagination/PaginationPaginatorTest.php delete mode 100644 tests/View/ViewBladeCompilerTest.php delete mode 100755 tests/View/ViewCompilerEngineTest.php delete mode 100755 tests/View/ViewEngineResolverTest.php delete mode 100755 tests/View/ViewFactoryTest.php delete mode 100755 tests/View/ViewFinderTest.php delete mode 100755 tests/View/ViewPhpEngineTest.php delete mode 100755 tests/View/ViewTest.php delete mode 100755 tests/View/fixtures/basic.php delete mode 100755 tests/View/fixtures/namespaced/basic.php delete mode 100755 tests/View/fixtures/nested/basic.php delete mode 100755 tests/View/fixtures/nested/child.php delete mode 100644 tests/View/fixtures/section-exception-layout.php delete mode 100644 tests/View/fixtures/section-exception.php diff --git a/composer.json b/composer.json index 02102660..83451dc2 100755 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "illuminate/reflection": "^13", "illuminate/session": "^13", "illuminate/support": "^13", + "illuminate/view": "^13", "ircmaxell/password-compat": "~1.0", "laravel/serializable-closure": "^2.0.10", "monolog/monolog": "^3.10", @@ -69,7 +70,6 @@ "illuminate/routing": "self.version", "illuminate/translation": "self.version", "illuminate/validation": "self.version", - "illuminate/view": "self.version", "illuminate/workbench": "self.version" }, "require-dev": { diff --git a/tests/Pagination/PaginationBootstrapPresenterTest.php b/tests/Pagination/PaginationBootstrapPresenterTest.php deleted file mode 100755 index e2b575ff..00000000 --- a/tests/Pagination/PaginationBootstrapPresenterTest.php +++ /dev/null @@ -1,174 +0,0 @@ -getPresenter(); - } - - - public function testSimpleRangeIsReturnedWhenCantBuildSlier() - { - $presenter = $this->getMock(BootstrapPresenter::class, ['getPageRange', 'getPrevious', 'getNext'], [$paginator = $this->getPaginator()] - ); - $presenter->expects($this->once())->method('getPageRange')->with($this->equalTo(1), $this->equalTo(2))->willReturn( - 'bar' - ); - $presenter->expects($this->once())->method('getPrevious')->willReturn('foo'); - $presenter->expects($this->once())->method('getNext')->willReturn('baz'); - - $this->assertEquals('foobarbaz', $presenter->render()); - } - - - public function testGetPageRange() - { - $presenter = $this->getPresenter(); - $presenter->setCurrentPage(1); - $content = $presenter->getPageRange(1, 2); - - $this->assertEquals('
  • 1
  • 2
  • ', $content); - } - - - public function testBeginningSliderIsCreatedWhenCloseToStart() - { - $presenter = $this->getMock(BootstrapPresenter::class, ['getPageRange', 'getPrevious', 'getNext', 'getStart', 'getFinish'], [$paginator = $this->getPaginator()] - ); - $presenter->setLastPage(14); - $presenter->expects($this->once())->method('getFinish')->willReturn('finish'); - $presenter->expects($this->once())->method('getPrevious')->willReturn('previous'); - $presenter->expects($this->once())->method('getNext')->willReturn('next'); - $presenter->expects($this->once())->method('getPageRange')->with($this->equalTo(1), $this->equalTo(8))->willReturn( - 'range' - ); - - $this->assertEquals('previousrangefinishnext', $presenter->render()); - } - - - public function testEndingSliderIsCreatedWhenCloseToStart() - { - $presenter = $this->getMock(BootstrapPresenter::class, ['getPageRange', 'getPrevious', 'getNext', 'getStart', 'getFinish'], [$paginator = $this->getPaginator()] - ); - $presenter->setLastPage(14); - $presenter->setCurrentPage(13); - $presenter->expects($this->once())->method('getStart')->willReturn('start'); - $presenter->expects($this->once())->method('getPrevious')->willReturn('previous'); - $presenter->expects($this->once())->method('getNext')->willReturn('next'); - $presenter->expects($this->once())->method('getPageRange')->with($this->equalTo(6), $this->equalTo(14))->willReturn( - 'range' - ); - - $this->assertEquals('previousstartrangenext', $presenter->render()); - } - - - public function testSliderIsCreatedWhenCloseToStart() - { - $presenter = $this->getMock(BootstrapPresenter::class, ['getPageRange', 'getPrevious', 'getNext', 'getStart', 'getFinish'], [$paginator = $this->getPaginator()] - ); - $presenter->setLastPage(30); - $presenter->setCurrentPage(15); - $presenter->expects($this->once())->method('getStart')->willReturn('start'); - $presenter->expects($this->once())->method('getFinish')->willReturn('finish'); - $presenter->expects($this->once())->method('getPrevious')->willReturn('previous'); - $presenter->expects($this->once())->method('getNext')->willReturn('next'); - $presenter->expects($this->once())->method('getPageRange')->with($this->equalTo(12), $this->equalTo(18))->willReturn( - 'range' - ); - - $this->assertEquals('previousstartrangefinishnext', $presenter->render()); - } - - - public function testPreviousLinkCanBeRendered() - { - $output = $this->getPresenter()->getPrevious(); - - $this->assertEquals('
  • «
  • ', $output); - - $presenter = $this->getPresenter(); - $presenter->setCurrentPage(2); - $output = $presenter->getPrevious(); - - $this->assertEquals('
  • ', $output); - } - - - public function testNextLinkCanBeRendered() - { - $presenter = $this->getPresenter(); - $presenter->setCurrentPage(2); - $output = $presenter->getNext(); - - $this->assertEquals('
  • »
  • ', $output); - - $presenter = $this->getPresenter(); - $presenter->setCurrentPage(1); - $output = $presenter->getNext(); - - $this->assertEquals('
  • ', $output); - } - - - public function testGetStart() - { - $presenter = $this->getPresenter(); - $output = $presenter->getStart(); - - $this->assertEquals('
  • 1
  • 2
  • ...
  • ', $output); - } - - - public function testGetFinish() - { - $presenter = $this->getPresenter(); - $output = $presenter->getFinish(); - - $this->assertEquals('
  • ...
  • 1
  • 2
  • ', $output); - } - - - public function testGetAdjacentRange() - { - $presenter = $this->getMock(BootstrapPresenter::class, ['getPageRange'], [$paginator = $this->getPaginator()]); - $presenter->expects($this->once())->method('getPageRange')->with($this->equalTo(1), $this->equalTo(7))->willReturn( - 'foo' - ); - $presenter->setCurrentPage(4); - - $this->assertEquals('foo', $presenter->getAdjacentRange()); - } - - - - protected function getPresenter() - { - return new BootstrapPresenter($this->getPaginator()); - } - - - protected function getPaginator() - { - $paginator = m::mock(Paginator::class); - $paginator->shouldReceive('lastPage')->once()->andReturn(2); - $paginator->shouldReceive('currentPage')->once()->andReturn(1); - $paginator->shouldReceive('getUrl')->andReturnUsing(function($page) { return 'http://foo.com?page='.$page; }); - return $paginator; - } - -} diff --git a/tests/Pagination/PaginationCustomPresenterTest.php b/tests/Pagination/PaginationCustomPresenterTest.php deleted file mode 100644 index dddb33b5..00000000 --- a/tests/Pagination/PaginationCustomPresenterTest.php +++ /dev/null @@ -1,52 +0,0 @@ -shouldReceive('getPageLinkWrapper') - ->once() - ->andReturnUsing(function($url, $page) { - return '' . $page . ''; - }); - - $this->assertEquals('1', $customPresenter->getPageLinkWrapper('http://laravel.com?page=1', '1', null)); - } - - - public function testGetDisabledTextWrapper() - { - $customPresenter = m::mock(Presenter::class); - $customPresenter->shouldReceive('getDisabledTextWrapper') - ->once() - ->andReturnUsing(function($text) { - return '
  • ' . $text . '
  • '; - }); - $this->assertEquals('
  • foo
  • ', $customPresenter->getDisabledTextWrapper('foo')); - } - - - public function testGetActiveTextWrapper() - { - $customPresenter = m::mock(Presenter::class); - $customPresenter->shouldReceive('getActiveTextWrapper') - ->once() - ->andReturnUsing(function($text) { - return '
  • ' . $text . '
  • '; - }); - $this->assertEquals('
  • bazzer
  • ', $customPresenter->getActiveTextWrapper('bazzer')); - } - -} diff --git a/tests/Pagination/PaginationFactoryTest.php b/tests/Pagination/PaginationFactoryTest.php deleted file mode 100755 index d3cce983..00000000 --- a/tests/Pagination/PaginationFactoryTest.php +++ /dev/null @@ -1,111 +0,0 @@ -getFactory(); - } - - - public function testPaginatorCanBeCreated() - { - $env = $this->getFactory(); - $request = Illuminate\Http\Request::create('http://foo.com', 'GET'); - $env->setRequest($request); - - $this->assertInstanceOf(Paginator::class, $env->make(['foo', 'bar'], 2, 2)); - } - - - public function testPaginationViewCanBeCreated() - { - $env = $this->getFactory(); - $paginator = m::mock(Paginator::class); - $env->getViewFactory()->shouldReceive('make')->once()->with('pagination::slider', ['environment' => $env, 'paginator' => $paginator] - )->andReturn('foo'); - - $this->assertEquals('foo', $env->getPaginationView($paginator)); - } - - - public function testCurrentPageCanBeRetrieved() - { - $env = $this->getFactory(); - $request = Illuminate\Http\Request::create('http://foo.com?page=2', 'GET'); - $env->setRequest($request); - - $this->assertEquals(2, $env->getCurrentPage()); - - $env = $this->getFactory(); - $request = Illuminate\Http\Request::create('http://foo.com?page=-1', 'GET'); - $env->setRequest($request); - - $this->assertEquals(1, $env->getCurrentPage()); - } - - - public function testSettingCurrentUrlOverrulesRequest() - { - $env = $this->getFactory(); - $request = Illuminate\Http\Request::create('http://foo.com?page=2', 'GET'); - $env->setRequest($request); - $env->setCurrentPage(3); - - $this->assertEquals(3, $env->getCurrentPage()); - } - - - public function testCurrentUrlCanBeRetrieved() - { - $env = $this->getFactory(); - $request = Illuminate\Http\Request::create('http://foo.com/bar?page=2', 'GET'); - $env->setRequest($request); - - $this->assertEquals('http://foo.com/bar', $env->getCurrentUrl()); - - $env = $this->getFactory(); - $request = Illuminate\Http\Request::create('http://foo.com?page=2', 'GET'); - $env->setRequest($request); - - $this->assertEquals('http://foo.com', $env->getCurrentUrl()); - } - - - public function testOverridingPageParam() - { - $env = $this->getFactory(); - $this->assertEquals('page', $env->getPageName()); - $env->setPageName('foo'); - $this->assertEquals('foo', $env->getPageName()); - } - - - protected function getFactory() - { - $request = m::mock(Request::class); - $view = m::mock(\Illuminate\View\Factory::class); - $trans = m::mock(TranslatorInterface::class); - $view->shouldReceive('addNamespace')->once()->with( - 'pagination', - realpath(__DIR__ . '/../../src/Illuminate/Pagination') . '/views' - ); - - return new Factory($request, $view, $trans, 'page'); - } - -} diff --git a/tests/Pagination/PaginationPaginatorTest.php b/tests/Pagination/PaginationPaginatorTest.php deleted file mode 100755 index c5a4a2fa..00000000 --- a/tests/Pagination/PaginationPaginatorTest.php +++ /dev/null @@ -1,227 +0,0 @@ -shouldReceive('getCurrentPage')->once()->andReturn(1); - $p->setupPaginationContext(); - - $this->assertEquals(2, $p->lastPage()); - $this->assertEquals(1, $p->currentPage()); - } - - - public function testPaginationContextIsSetupCorrectlyWithEmptyItems() - { - $p = new Paginator($factory = m::mock(Factory::class), [], 0, 2); - $factory->shouldReceive('getCurrentPage')->once()->andReturn(1); - $p->setupPaginationContext(); - - $this->assertEquals(1, $p->lastPage()); - $this->assertEquals(1, $p->currentPage()); - } - - - public function testSimplePagination() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 2); - $factory->shouldReceive('getCurrentPage')->once()->andReturn(1); - $p->setupPaginationContext(); - - $this->assertEquals(2, $p->lastPage()); - $this->assertEquals(1, $p->currentPage()); - $this->assertEquals(['foo', 'bar'], $p->items()); - } - - - public function testSimplePaginationLastPage() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3); - $factory->shouldReceive('getCurrentPage')->once()->andReturn(1); - $p->setupPaginationContext(); - - $this->assertEquals(1, $p->lastPage()); - $this->assertEquals(1, $p->currentPage()); - $this->assertCount(3, $p->items()); - } - - - public function testPaginationContextIsSetupCorrectlyInCursorMode() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 2); - $factory->shouldReceive('getCurrentPage')->once()->andReturn(1); - $p->setupPaginationContext(); - - $this->assertEquals(2, $p->lastPage()); - $this->assertEquals(1, $p->currentPage()); - } - - - public function testPaginationContextSetsUpRangeCorrectly() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $factory->shouldReceive('getCurrentPage')->once()->andReturn(1); - $p->setupPaginationContext(); - - $this->assertEquals(1, $p->firstItem()); - $this->assertEquals(2, $p->lastItem()); - } - - - public function testPaginationContextHandlesHugeCurrentPage() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $factory->shouldReceive('getCurrentPage')->once()->andReturn(15); - $p->setupPaginationContext(); - - $this->assertEquals(2, $p->lastPage()); - $this->assertEquals(2, $p->currentPage()); - } - - - public function testPaginationContextHandlesPageLessThanOne() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $factory->shouldReceive('getCurrentPage')->once()->andReturn(-1); - $p->setupPaginationContext(); - - $this->assertEquals(2, $p->lastPage()); - $this->assertEquals(1, $p->currentPage()); - } - - - public function testPaginationContextHandlesPageLessThanOneAsString() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $factory->shouldReceive('getCurrentPage')->once()->andReturn('-1'); - $p->setupPaginationContext(); - - $this->assertEquals(2, $p->lastPage()); - $this->assertEquals(1, $p->currentPage()); - } - - - public function testPaginationContextHandlesPageInvalidFormat() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $factory->shouldReceive('getCurrentPage')->once()->andReturn('abc'); - $p->setupPaginationContext(); - - $this->assertEquals(2, $p->lastPage()); - $this->assertEquals(1, $p->currentPage()); - } - - - public function testPaginationContextHandlesPageMissing() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $factory->shouldReceive('getCurrentPage')->once()->andReturn(null); - $p->setupPaginationContext(); - - $this->assertEquals(2, $p->lastPage()); - $this->assertEquals(1, $p->currentPage()); - } - - - public function testGetLinksCallsEnvironmentProperly() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $factory->shouldReceive('getPaginationView')->once()->with($p, null)->andReturn('foo'); - - $this->assertEquals('foo', $p->links()); - } - - - public function testGetUrlProperlyFormatsUrl() - { - $p = new Paginator($env = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $env->shouldReceive('getCurrentUrl')->andReturn('http://foo.com'); - $env->shouldReceive('getPageName')->andReturn('page'); - - $this->assertEquals('http://foo.com?page=1', $p->getUrl(1)); - $p->addQuery('foo', 'bar'); - $this->assertEquals('http://foo.com?foo=bar&page=1', $p->getUrl(1)); - } - - - public function testEnvironmentAccess() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $this->assertInstanceOf(Factory::class, $p->getFactory()); - } - - - public function testPaginatorIsCountable() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - - $this->assertCount(3, $p); - } - - - public function testPaginatorIsIterable() - { - $p = new Paginator($factory = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - - $this->assertInstanceOf('ArrayIterator', $p->getIterator()); - $this->assertEquals(['foo', 'bar', 'baz'], $p->getIterator()->getArrayCopy()); - } - - - public function testGetUrlAddsFragment() - { - $p = new Paginator($env = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $env->shouldReceive('getCurrentUrl')->andReturn('http://foo.com'); - $env->shouldReceive('getPageName')->andReturn('page'); - - $p->fragment("a-fragment"); - - $this->assertEquals('http://foo.com?page=1#a-fragment', $p->getUrl(1)); - $p->addQuery('foo', 'bar'); - $this->assertEquals('http://foo.com?foo=bar&page=1#a-fragment', $p->getUrl(1)); - } - - - public function testGetUrlHasPriorityOverAppends() - { - $p = new Paginator($env = m::mock(Factory::class), ['foo', 'bar', 'baz'], 3, 2); - $env->shouldReceive('getCurrentUrl')->andReturn('http://foo.com'); - $env->shouldReceive('getPageName')->andReturn('page'); - - $p->appends([ - 'sort' => 'asc', - 'page' => 2, - ]); - $this->assertEquals('http://foo.com?sort=asc&page=1', $p->getUrl(1)); - - $p->appends([ - 'sort' => 'desc', - 'page' => '2', - ]); - $this->assertEquals('http://foo.com?sort=desc&page=1', $p->getUrl(1)); - } - - - public function testPaginatorDecoratesCollection() - { - $p = new Paginator(m::mock(Factory::class), ['a', 'b', 'c'], 3, 2); - $last = $p->last(); - - $this->assertEquals('c', $last); - } - -} diff --git a/tests/Support/SupportUtilTest.php b/tests/Support/SupportUtilTest.php index 016058b1..b6d237dd 100644 --- a/tests/Support/SupportUtilTest.php +++ b/tests/Support/SupportUtilTest.php @@ -2,7 +2,6 @@ namespace Illuminate\Tests\Support; -use Illuminate\Pagination\Factory; use Illuminate\Pagination\Paginator; use Illuminate\Support\Util; use L4\Tests\BackwardCompatibleTestCase; @@ -42,11 +41,7 @@ public function isValueEmpty(): void */ public function isEmptyOnEmptyPaginatorObject(): void { - $pagination = new Paginator( - $this->prophesize(Factory::class)->reveal(), - [], - 0 - ); + $pagination = new Paginator([], 15); $this->assertTrue(Util::isEmpty($pagination)); } @@ -56,11 +51,7 @@ public function isEmptyOnEmptyPaginatorObject(): void */ public function isEmptyOnNonEmptyPaginatorObject(): void { - $pagination = new Paginator( - $this->prophesize(Factory::class)->reveal(), - ['1', '2', '3'], - 3 - ); + $pagination = new Paginator(['1', '2', '3'], 15); $this->assertFalse(Util::isEmpty($pagination)); } diff --git a/tests/View/ViewBladeCompilerTest.php b/tests/View/ViewBladeCompilerTest.php deleted file mode 100644 index 5ac6efd3..00000000 --- a/tests/View/ViewBladeCompilerTest.php +++ /dev/null @@ -1,627 +0,0 @@ -compiler = new BladeCompiler($this->getFiles(), __DIR__); - } - - protected function tearDown(): void - { - m::close(); - } - - - public function testIsExpiredReturnsTrueIfCompiledFileDoesntExist() - { - $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); - $files->shouldReceive('exists')->once()->with(__DIR__ . '/' . md5('foo'))->andReturn(false); - $this->assertTrue($compiler->isExpired('foo')); - } - - - public function testIsExpiredReturnsTrueIfCachePathIsNull() - { - $compiler = new BladeCompiler($files = $this->getFiles(), null); - $files->shouldReceive('exists')->never(); - $this->assertTrue($compiler->isExpired('foo')); - } - - - public function testIsExpiredReturnsTrueWhenModificationTimesWarrant() - { - $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); - $files->shouldReceive('exists')->once()->with(__DIR__.'/'.md5('foo'))->andReturn(true); - $files->shouldReceive('lastModified')->once()->with('foo')->andReturn(100); - $files->shouldReceive('lastModified')->once()->with(__DIR__.'/'.md5('foo'))->andReturn(0); - $this->assertTrue($compiler->isExpired('foo')); - } - - - public function testCompilePathIsProperlyCreated() - { - $compiler = new BladeCompiler($this->getFiles(), __DIR__); - $this->assertEquals(__DIR__.'/'.md5('foo'), $compiler->getCompiledPath('foo')); - } - - - public function testCompileCompilesFileAndReturnsContents() - { - $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); - $files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World'); - $files->shouldReceive('put')->once()->with(__DIR__.'/'.md5('foo'), 'Hello World'); - $compiler->compile('foo'); - } - - - public function testCompileCompilesAndGetThePath() - { - $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); - $files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World'); - $files->shouldReceive('put')->once()->with(__DIR__.'/'.md5('foo'), 'Hello World'); - $compiler->compile('foo'); - $this->assertEquals('foo', $compiler->getPath()); - } - - - public function testCompileSetAndGetThePath() - { - $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); - $compiler->setPath('foo'); - $this->assertEquals('foo', $compiler->getPath()); - } - - - public function testCompileDoesntStoreFilesWhenCachePathIsNull() - { - $compiler = new BladeCompiler($files = $this->getFiles(), null); - $files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World'); - $files->shouldReceive('put')->never(); - $compiler->compile('foo'); - } - - - public function testEchosAreCompiled() - { - $this->assertEquals('', $this->compiler->compileString('{{{$name}}}')); - $this->assertEquals('', $this->compiler->compileString('{{$name}}')); - $this->assertEquals('', $this->compiler->compileString('{{ $name }}')); - $this->assertEquals('', $this->compiler->compileString('{{ - $name - }}')); - $this->assertEquals("\n\n", $this->compiler->compileString("{{ \$name }}\n")); - $this->assertEquals("\r\n\r\n", $this->compiler->compileString("{{ \$name }}\r\n")); - $this->assertEquals("\n\n", $this->compiler->compileString("{{ \$name }}\n")); - $this->assertEquals("\r\n\r\n", $this->compiler->compileString("{{ \$name }}\r\n")); - - $this->assertEquals('', $this->compiler->compileString('{{ $name or "foo" }}')); - $this->assertEquals('name) ? $user->name : "foo"; ?>', $this->compiler->compileString('{{ $user->name or "foo" }}')); - $this->assertEquals('', $this->compiler->compileString('{{$name or "foo"}}')); - $this->assertEquals('', $this->compiler->compileString('{{ - $name or "foo" - }}')); - - $this->assertEquals('', $this->compiler->compileString('{{ $name or \'foo\' }}')); - $this->assertEquals('', $this->compiler->compileString('{{$name or \'foo\'}}')); - $this->assertEquals('', $this->compiler->compileString('{{ - $name or \'foo\' - }}')); - - $this->assertEquals('', $this->compiler->compileString('{{ $age or 90 }}')); - $this->assertEquals('', $this->compiler->compileString('{{$age or 90}}')); - $this->assertEquals('', $this->compiler->compileString('{{ - $age or 90 - }}')); - - $this->assertEquals('', $this->compiler->compileString('{{ "Hello world or foo" }}')); - $this->assertEquals('', $this->compiler->compileString('{{"Hello world or foo"}}')); - $this->assertEquals('', $this->compiler->compileString('{{$foo + $or + $baz}}')); - $this->assertEquals('', $this->compiler->compileString('{{ - "Hello world or foo" - }}')); - - $this->assertEquals('', $this->compiler->compileString('{{ \'Hello world or foo\' }}')); - $this->assertEquals('', $this->compiler->compileString('{{\'Hello world or foo\'}}')); - $this->assertEquals('', $this->compiler->compileString('{{ - \'Hello world or foo\' - }}')); - - $this->assertEquals('', $this->compiler->compileString('{{ myfunc(\'foo or bar\') }}')); - $this->assertEquals('', $this->compiler->compileString('{{ myfunc("foo or bar") }}')); - $this->assertEquals('', $this->compiler->compileString('{{ myfunc("$name or \'foo\'") }}')); - } - - - public function testEscapedWithAtEchosAreCompiled() - { - $this->assertEquals('{{$name}}', $this->compiler->compileString('@{{$name}}')); - $this->assertEquals('{{ $name }}', $this->compiler->compileString('@{{ $name }}')); - $this->assertEquals('{{ - $name - }}', - $this->compiler->compileString('@{{ - $name - }}')); - $this->assertEquals('{{ $name }} - ', - $this->compiler->compileString('@{{ $name }} - ')); - } - - - public function testReversedEchosAreCompiled() - { - $this->compiler->setEscapedContentTags('{{', '}}'); - $this->compiler->setContentTags('{{{', '}}}'); - $this->assertEquals('', $this->compiler->compileString('{{$name}}')); - $this->assertEquals('', $this->compiler->compileString('{{{$name}}}')); - $this->assertEquals('', $this->compiler->compileString('{{{ $name }}}')); - $this->assertEquals('', $this->compiler->compileString('{{{ - $name - }}}')); - } - - - public function testExtendsAreCompiled() - { - $compiler = new BladeCompiler($this->getFiles(), __DIR__); - $string = '@extends(\'foo\') -test'; - $expected = "test".PHP_EOL.'make(\'foo\', array_except(get_defined_vars(), array(\'__data\', \'__path\')))->render(); ?>'; - $this->assertEquals($expected, $compiler->compileString($string)); - - - $compiler = new BladeCompiler($this->getFiles(), __DIR__); - $string = '@extends(name(foo))'.PHP_EOL.'test'; - $expected = "test".PHP_EOL.'make(name(foo), array_except(get_defined_vars(), array(\'__data\', \'__path\')))->render(); ?>'; - $this->assertEquals($expected, $compiler->compileString($string)); - } - - - public function testPushIsCompiled() - { - $string = '@push(\'foo\') -test -@endpush'; - $expected = 'startSection(\'foo\'); ?> -test -appendSection(); ?>'; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testStackIsCompiled() - { - $string = '@stack(\'foo\')'; - $expected = 'yieldContent(\'foo\'); ?>'; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testCommentsAreCompiled() - { - $string = '{{--this is a comment--}}'; - $expected = ''; - $this->assertEquals($expected, $this->compiler->compileString($string)); - - - $string = '{{-- -this is a comment ---}}'; - $expected = ''; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testIfStatementsAreCompiled() - { - $string = '@if (name(foo(bar))) -breeze -@endif'; - $expected = ' -breeze -'; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testElseStatementsAreCompiled() - { - $string = '@if (name(foo(bar))) -breeze -@else -boom -@endif'; - $expected = ' -breeze - -boom -'; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testElseIfStatementsAreCompiled() - { - $string = '@if(name(foo(bar))) -breeze -@elseif(boom(breeze)) -boom -@endif'; - $expected = ' -breeze - -boom -'; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testUnlessStatementsAreCompiled() - { - $string = '@unless (name(foo(bar))) -breeze -@endunless'; - $expected = ' -breeze -'; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testForelseStatementsAreCompiled() - { - $string = '@forelse ($this->getUsers() as $user) -breeze -@empty -empty -@endforelse'; - $expected = 'getUsers() as $user): $__empty_1 = false; ?> -breeze - -empty -'; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testNestedForelseStatementsAreCompiled() - { - $string = '@forelse ($this->getUsers() as $user) -@forelse ($user->tags as $tag) -breeze -@empty -tag empty -@endforelse -@empty -empty -@endforelse'; - $expected = 'getUsers() as $user): $__empty_1 = false; ?> -tags as $tag): $__empty_2 = false; ?> -breeze - -tag empty - - -empty -'; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testStatementThatContainsNonConsecutiveParanthesisAreCompiled() - { - $string = "Foo @lang(function_call('foo(blah)')) bar"; - $expected = "Foo bar"; - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testIncludesAreCompiled() - { - $this->assertEquals('make(\'foo\', array_except(get_defined_vars(), array(\'__data\', \'__path\')))->render(); ?>', $this->compiler->compileString('@include(\'foo\')')); - $this->assertEquals('make(name(foo), array_except(get_defined_vars(), array(\'__data\', \'__path\')))->render(); ?>', $this->compiler->compileString('@include(name(foo))')); - } - - - public function testShowEachAreCompiled() - { - $this->assertEquals('renderEach(\'foo\', \'bar\'); ?>', $this->compiler->compileString('@each(\'foo\', \'bar\')')); - $this->assertEquals('renderEach(name(foo)); ?>', $this->compiler->compileString('@each(name(foo))')); - } - - - public function testYieldsAreCompiled() - { - $this->assertEquals('yieldContent(\'foo\'); ?>', $this->compiler->compileString('@yield(\'foo\')')); - $this->assertEquals('yieldContent(\'foo\', \'bar\'); ?>', $this->compiler->compileString('@yield(\'foo\', \'bar\')')); - $this->assertEquals('yieldContent(name(foo)); ?>', $this->compiler->compileString('@yield(name(foo))')); - } - - - public function testShowsAreCompiled() - { - $this->assertEquals('yieldSection(); ?>', $this->compiler->compileString('@show')); - } - - - public function testLanguageAndChoicesAreCompiled() - { - $this->assertEquals('', $this->compiler->compileString("@lang('foo')")); - $this->assertEquals('', $this->compiler->compileString("@choice('foo', 1)")); - } - - - public function testSectionStartsAreCompiled() - { - $this->assertEquals('startSection(\'foo\'); ?>', $this->compiler->compileString('@section(\'foo\')')); - $this->assertEquals('startSection(name(foo)); ?>', $this->compiler->compileString('@section(name(foo))')); - } - - - public function testStopSectionsAreCompiled() - { - $this->assertEquals('stopSection(); ?>', $this->compiler->compileString('@stop')); - } - - - public function testEndSectionsAreCompiled() - { - $this->assertEquals('stopSection(); ?>', $this->compiler->compileString('@endsection')); - } - - - public function testAppendSectionsAreCompiled() - { - $this->assertEquals('appendSection(); ?>', $this->compiler->compileString('@append')); - } - - - public function testCustomPhpCodeIsCorrectlyHandled() - { - $this->assertEquals(' ', $this->compiler->compileString("@if(\$test) @endif")); - } - - - public function testMixingYieldAndEcho() - { - $this->assertEquals('yieldContent(\'title\'); ?> - ', $this->compiler->compileString("@yield('title') - {{Config::get('site.title')}}")); - } - - - public function testCustomExtensionsAreCompiled() - { - $this->compiler->extend(function($value) { return str_replace('foo', 'bar', $value); }); - $this->assertEquals('bar', $this->compiler->compileString('foo')); - } - - - public function testConfiguringContentTags() - { - $this->compiler->setContentTags('[[', ']]'); - $this->compiler->setEscapedContentTags('[[[', ']]]'); - - $this->assertEquals('', $this->compiler->compileString('[[[ $name ]]]')); - $this->assertEquals('', $this->compiler->compileString('[[ $name ]]')); - $this->assertEquals('', $this->compiler->compileString('[[ - $name - ]]')); - } - - - public function testExpressionsOnTheSameLine() - { - $this->assertEquals(' space () ', $this->compiler->compileString('@lang(foo(bar(baz(qux(breeze()))))) space () @lang(foo(bar))')); - } - - - public function testExpressionWithinHTML() - { - $this->assertEquals('>', $this->compiler->compileString('')); - $this->assertEquals('>', $this->compiler->compileString('')); - $this->assertEquals(' >', $this->compiler->compileString('')); - } - - - public function testSelectedStatementsAreCompiled() - { - $string = ''; - $expected = "/>"; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testCheckedStatementsAreCompiled() - { - $string = ''; - $expected = "/>"; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testDisabledStatementsAreCompiled() - { - $string = ''; - $expected = ""; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testClassesAreConditionallyCompiledFromArray() - { - $string = " true, 'mr-2' => false])>"; - $expected = " true, 'mr-2' => false])); ?>\">"; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testJsonIsCompiledWithSafeDefaultEncodingOptions() - { - $string = 'var foo = @json($var);'; - $expected = 'var foo = ;'; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testEnvStatementsAreCompiled() - { - $string = "@env('staging') -breeze -@else -boom -@endenv"; - $expected = "environment('staging')): ?> -breeze - -boom -"; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testEnvStatementsWithMultipleStringParamsAreCompiled() - { - $string = "@env('staging', 'production') -breeze -@else -boom -@endenv"; - $expected = "environment('staging', 'production')): ?> -breeze - -boom -"; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testEnvStatementsWithArrayParamAreCompiled() - { - $string = "@env(['staging', 'production']) -breeze -@else -boom -@endenv"; - $expected = "environment(['staging', 'production'])): ?> -breeze - -boom -"; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testProductionEnvStatementsAreCompiled() - { - $string = "@production -breeze -@else -boom -@endproduction"; - $expected = "environment('production')): ?> -breeze - -boom -"; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testPlainAuthIfStatementsAreCompiled() - { - $string = '@auth -breeze -@endauth'; - $expected = ' -breeze -'; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - public function testPlainGuestIfStatementsAreCompiled() - { - $string = '@guest -breeze -@endguest'; - $expected = ' -breeze -'; - - $this->assertEquals($expected, $this->compiler->compileString($string)); - } - - - protected function getFiles() - { - return m::mock('Illuminate\Filesystem\Filesystem'); - } - - - public function testRetrieveDefaultContentTags() - { - $this->assertEquals(['{{', '}}'], $this->compiler->getContentTags()); - } - - - public function testRetrieveDefaultEscapedContentTags() - { - $this->assertEquals(['{{{', '}}}'], $this->compiler->getEscapedContentTags()); - } - - - /** - * @dataProvider testGetTagsProvider() - */ - public function testSetAndRetrieveContentTags($openingTag, $closingTag) - { - $this->compiler->setContentTags($openingTag, $closingTag); - $this->assertSame([$openingTag, $closingTag], $this->compiler->getContentTags()); - } - - - /** - * @dataProvider testGetTagsProvider() - */ - public function testSetAndRetrieveEscapedContentTags($openingTag, $closingTag) - { - $this->compiler->setEscapedContentTags($openingTag, $closingTag); - $this->assertSame([$openingTag, $closingTag], $this->compiler->getEscapedContentTags()); - } - - - public function testGetTagsProvider() - { - return [ - ['{{', '}}'], - ['{{{', '}}}'], - ['[[', ']]'], - ['[[[', ']]]'], - ['((', '))'], - ['(((', ')))'], - ]; - } - -} diff --git a/tests/View/ViewCompilerEngineTest.php b/tests/View/ViewCompilerEngineTest.php deleted file mode 100755 index 0d388ee5..00000000 --- a/tests/View/ViewCompilerEngineTest.php +++ /dev/null @@ -1,50 +0,0 @@ -getEngine(); - $engine->getCompiler()->shouldReceive('getCompiledPath')->with(__DIR__ . '/fixtures/foo.php')->andReturn( - __DIR__ . '/fixtures/basic.php' - ); - $engine->getCompiler()->shouldReceive('isExpired')->once()->with(__DIR__ . '/fixtures/foo.php')->andReturn( - true - ); - $engine->getCompiler()->shouldReceive('compile')->once()->with(__DIR__.'/fixtures/foo.php'); - $results = $engine->get(__DIR__.'/fixtures/foo.php'); - - $this->assertEquals("Hello World\n", $results); - } - - - public function testViewsAreNotRecompiledIfTheyAreNotExpired() - { - $engine = $this->getEngine(); - $engine->getCompiler()->shouldReceive('getCompiledPath')->with(__DIR__.'/fixtures/foo.php')->andReturn(__DIR__.'/fixtures/basic.php'); - $engine->getCompiler()->shouldReceive('isExpired')->once()->andReturn(false); - $engine->getCompiler()->shouldReceive('compile')->never(); - $results = $engine->get(__DIR__.'/fixtures/foo.php'); - - $this->assertEquals("Hello World\n", $results); - } - - - protected function getEngine() - { - return new CompilerEngine(m::mock(CompilerInterface::class)); - } - -} diff --git a/tests/View/ViewEngineResolverTest.php b/tests/View/ViewEngineResolverTest.php deleted file mode 100755 index 92d5d997..00000000 --- a/tests/View/ViewEngineResolverTest.php +++ /dev/null @@ -1,24 +0,0 @@ -register('foo', function() { return new StdClass; }); - $result = $resolver->resolve('foo'); - - $this->assertEquals(spl_object_hash($result), spl_object_hash($resolver->resolve('foo'))); - } - - - public function testResolverThrowsExceptionOnUnknownEngine() - { - $this->expectException('InvalidArgumentException'); - $resolver = new Illuminate\View\Engines\EngineResolver; - $resolver->resolve('foo'); - } - -} diff --git a/tests/View/ViewFactoryTest.php b/tests/View/ViewFactoryTest.php deleted file mode 100755 index 83ee781b..00000000 --- a/tests/View/ViewFactoryTest.php +++ /dev/null @@ -1,378 +0,0 @@ -getFactory(); - $factory->getFinder()->shouldReceive('find')->once()->with('view')->andReturn('path.php'); - $factory->getEngineResolver()->shouldReceive('resolve')->once()->with('php')->andReturn($engine = m::mock( - EngineInterface::class - )); - $factory->getFinder()->shouldReceive('addExtension')->once()->with('php'); - $factory->setDispatcher(new Illuminate\Events\Dispatcher); - $factory->creator('view', function($view) { $_SERVER['__test.view'] = $view; }); - $factory->addExtension('php', 'php'); - $view = $factory->make('view', ['foo' => 'bar'], ['baz' => 'boom']); - - $this->assertSame($engine, $view->getEngine()); - $this->assertSame($_SERVER['__test.view'], $view); - - unset($_SERVER['__test.view']); - } - - - public function testExistsPassesAndFailsViews() - { - $factory = $this->getFactory(); - $factory->getFinder()->shouldReceive('find')->once()->with('foo')->andThrow('InvalidArgumentException'); - $factory->getFinder()->shouldReceive('find')->once()->with('bar')->andReturn('path.php'); - - $this->assertFalse($factory->exists('foo')); - $this->assertTrue($factory->exists('bar')); - } - - - public function testRenderEachCreatesViewForEachItemInArray() - { - $factory = m::mock('Illuminate\View\Factory[make]', $this->getFactoryArgs()); - $factory->shouldReceive('make')->once()->with('foo', ['key' => 'bar', 'value' => 'baz'])->andReturn($mockView1 = m::mock('StdClass')); - $factory->shouldReceive('make')->once()->with('foo', ['key' => 'breeze', 'value' => 'boom'])->andReturn($mockView2 = m::mock('StdClass')); - $mockView1->shouldReceive('render')->once()->andReturn('dayle'); - $mockView2->shouldReceive('render')->once()->andReturn('rees'); - - $result = $factory->renderEach('foo', ['bar' => 'baz', 'breeze' => 'boom'], 'value'); - - $this->assertEquals('daylerees', $result); - } - - - public function testEmptyViewsCanBeReturnedFromRenderEach() - { - $factory = m::mock('Illuminate\View\Factory[make]', $this->getFactoryArgs()); - $factory->shouldReceive('make')->once()->with('foo')->andReturn($mockView = m::mock('StdClass')); - $mockView->shouldReceive('render')->once()->andReturn('empty'); - - $this->assertEquals('empty', $factory->renderEach('view', [], 'iterator', 'foo')); - } - - - public function testRawStringsMayBeReturnedFromRenderEach() - { - $this->assertEquals('foo', $this->getFactory()->renderEach('foo', [], 'item', 'raw|foo')); - } - - - public function testEnvironmentAddsExtensionWithCustomResolver() - { - $factory = $this->getFactory(); - - $resolver = function(){}; - - $factory->getFinder()->shouldReceive('addExtension')->once()->with('foo'); - $factory->getEngineResolver()->shouldReceive('register')->once()->with('bar', $resolver); - $factory->getFinder()->shouldReceive('find')->once()->with('view')->andReturn('path.foo'); - $factory->getEngineResolver()->shouldReceive('resolve')->once()->with('bar')->andReturn($engine = m::mock( - EngineInterface::class - )); - $factory->getDispatcher()->shouldReceive('dispatch'); - - $factory->addExtension('foo', 'bar', $resolver); - - $view = $factory->make('view', ['data']); - $this->assertSame($engine, $view->getEngine()); - } - - - public function testAddingExtensionPrependsNotAppends() - { - $factory = $this->getFactory(); - $factory->getFinder()->shouldReceive('addExtension')->once()->with('foo'); - - $factory->addExtension('foo', 'bar'); - - $extensions = $factory->getExtensions(); - $this->assertEquals('bar', reset($extensions)); - $this->assertEquals('foo', key($extensions)); - } - - - public function testPrependedExtensionOverridesExistingExtensions() - { - $factory = $this->getFactory(); - $factory->getFinder()->shouldReceive('addExtension')->once()->with('foo'); - $factory->getFinder()->shouldReceive('addExtension')->once()->with('baz'); - - $factory->addExtension('foo', 'bar'); - $factory->addExtension('baz', 'bar'); - - $extensions = $factory->getExtensions(); - $this->assertEquals('bar', reset($extensions)); - $this->assertEquals('baz', key($extensions)); - } - - - public function testComposersAreProperlyRegistered() - { - $factory = $this->getFactory(); - $factory->getDispatcher()->shouldReceive('listen')->once()->with('composing: foo', m::type('Closure')); - $callback = $factory->composer('foo', function() { return 'bar'; }); - $callback = $callback[0]; - - $this->assertEquals('bar', $callback()); - } - - - public function testComposersAreProperlyRegisteredWithPriority() - { - $factory = $this->getFactory(); - $factory->getDispatcher()->shouldReceive('listen')->once()->with('composing: foo', m::type('Closure'), 1); - $callback = $factory->composer('foo', function() { return 'bar'; }, 1); - $callback = $callback[0]; - - $this->assertEquals('bar', $callback()); - } - - - public function testComposersCanBeMassRegistered() - { - $factory = $this->getFactory(); - $factory->getDispatcher()->shouldReceive('listen')->once()->with('composing: bar', m::type('Closure')); - $factory->getDispatcher()->shouldReceive('listen')->once()->with('composing: qux', m::type('Closure')); - $factory->getDispatcher()->shouldReceive('listen')->once()->with('composing: foo', m::type('Closure')); - $composers = $factory->composers([ - 'foo' => 'bar', - 'baz@baz' => ['qux', 'foo'], - ]); - - $this->assertCount(3, $composers); - $reflections = [ - new ReflectionFunction($composers[0]), - new ReflectionFunction($composers[1]), - ]; - $this->assertEquals(['class' => 'foo', 'method' => 'compose', 'container' => null], $reflections[0]->getStaticVariables()); - $this->assertEquals(['class' => 'baz', 'method' => 'baz', 'container' => null], $reflections[1]->getStaticVariables()); - } - - - public function testClassCallbacks() - { - $factory = $this->getFactory(); - $factory->getDispatcher()->shouldReceive('listen')->once()->with('composing: foo', m::type('Closure')); - $factory->setContainer($container = m::mock(Container::class)); - $container->shouldReceive('make')->once()->with('FooComposer')->andReturn($composer = m::mock('StdClass')); - $composer->shouldReceive('compose')->once()->with('view')->andReturn('composed'); - $callback = $factory->composer('foo', 'FooComposer'); - $callback = $callback[0]; - - $this->assertEquals('composed', $callback('view')); - } - - - public function testClassCallbacksWithMethods() - { - $factory = $this->getFactory(); - $factory->getDispatcher()->shouldReceive('listen')->once()->with('composing: foo', m::type('Closure')); - $factory->setContainer($container = m::mock(Container::class)); - $container->shouldReceive('make')->once()->with('FooComposer')->andReturn($composer = m::mock('StdClass')); - $composer->shouldReceive('doComposer')->once()->with('view')->andReturn('composed'); - $callback = $factory->composer('foo', 'FooComposer@doComposer'); - $callback = $callback[0]; - - $this->assertEquals('composed', $callback('view')); - } - - - public function testCallComposerCallsProperEvent() - { - $factory = $this->getFactory(); - $view = m::mock(View::class); - $view->shouldReceive('getName')->once()->andReturn('name'); - $factory->getDispatcher()->shouldReceive('dispatch')->once()->with('composing: name', [$view]); - - $factory->callComposer($view); - } - - - public function testRenderCountHandling() - { - $factory = $this->getFactory(); - $factory->incrementRender(); - $this->assertFalse($factory->doneRendering()); - $factory->decrementRender(); - $this->assertTrue($factory->doneRendering()); - } - - - public function testBasicSectionHandling() - { - $factory = $this->getFactory(); - $factory->startSection('foo'); - echo 'hi'; - $factory->stopSection(); - $this->assertEquals('hi', $factory->yieldContent('foo')); - } - - - public function testSectionExtending() - { - $factory = $this->getFactory(); - $factory->startSection('foo'); - echo 'hi @parent'; - $factory->stopSection(); - $factory->startSection('foo'); - echo 'there'; - $factory->stopSection(); - $this->assertEquals('hi there', $factory->yieldContent('foo')); - } - - - public function testSingleStackPush() - { - $factory = $this->getFactory(); - $factory->startSection('foo'); - echo 'hi'; - $factory->appendSection(); - $this->assertEquals('hi', $factory->yieldContent('foo')); - } - - - public function testMultipleStackPush() - { - $factory = $this->getFactory(); - $factory->startSection('foo'); - echo 'hi'; - $factory->appendSection(); - $factory->startSection('foo'); - echo ', Hello!'; - $factory->appendSection(); - $this->assertEquals('hi, Hello!', $factory->yieldContent('foo')); - } - - - public function testSessionAppending() - { - $factory = $this->getFactory(); - $factory->startSection('foo'); - echo 'hi'; - $factory->appendSection(); - $factory->startSection('foo'); - echo 'there'; - $factory->appendSection(); - $this->assertEquals('hithere', $factory->yieldContent('foo')); - } - - - public function testYieldSectionStopsAndYields() - { - $factory = $this->getFactory(); - $factory->startSection('foo'); - echo 'hi'; - $this->assertEquals('hi', $factory->yieldSection()); - } - - - public function testInjectStartsSectionWithContent() - { - $factory = $this->getFactory(); - $factory->inject('foo', 'hi'); - $this->assertEquals('hi', $factory->yieldContent('foo')); - } - - - public function testEmptyStringIsReturnedForNonSections() - { - $factory = $this->getFactory(); - $this->assertEquals('', $factory->yieldContent('foo')); - } - - - public function testSectionFlushing() - { - $factory = $this->getFactory(); - $factory->startSection('foo'); - echo 'hi'; - $factory->stopSection(); - - $this->assertCount(1, $factory->getSections()); - - $factory->flushSections(); - - $this->assertCount(0, $factory->getSections()); - } - - - public function testExceptionIsThrownForUnknownExtension() - { - $this->expectException('InvalidArgumentException'); - $factory = $this->getFactory(); - $factory->getFinder()->shouldReceive('find')->once()->with('view')->andReturn('view.foo'); - $factory->make('view'); - } - - - public function testExceptionsInSectionsAreThrown() - { - $engine = new CompilerEngine(m::mock(CompilerInterface::class)); - $engine->getCompiler()->shouldReceive('getCompiledPath')->andReturnUsing( - function ($path) { - return $path; - } - ); - $engine->getCompiler()->shouldReceive('isExpired')->twice()->andReturn(false); - $factory = $this->getFactory(); - $factory->getEngineResolver()->shouldReceive('resolve')->twice()->andReturn($engine); - $factory->getFinder()->shouldReceive('find')->once()->with('layout')->andReturn( - __DIR__ . '/fixtures/section-exception-layout.php' - ); - $factory->getFinder()->shouldReceive('find')->once()->with('view')->andReturn( - __DIR__ . '/fixtures/section-exception.php' - ); - $factory->getDispatcher()->shouldReceive('dispatch')->times(4); - - $this->expectException('Exception', 'section exception message'); - $factory->make('view')->render(); - } - - - protected function getFactory() - { - return new Factory( - m::mock(EngineResolver::class), - m::mock(ViewFinderInterface::class), - m::mock(Dispatcher::class) - ); - } - - - protected function getFactoryArgs() - { - return [ - m::mock(EngineResolver::class), - m::mock(ViewFinderInterface::class), - m::mock(Dispatcher::class), - ]; - } - -} diff --git a/tests/View/ViewFinderTest.php b/tests/View/ViewFinderTest.php deleted file mode 100755 index 8987a221..00000000 --- a/tests/View/ViewFinderTest.php +++ /dev/null @@ -1,155 +0,0 @@ -getFinder(); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__ . '/foo.blade.php')->andReturn(true); - - $this->assertEquals(__DIR__.'/foo.blade.php', $finder->find('foo')); - } - - - public function testCascadingFileLoading() - { - $finder = $this->getFinder(); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/foo.blade.php')->andReturn(false); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/foo.php')->andReturn(true); - - $this->assertEquals(__DIR__.'/foo.php', $finder->find('foo')); - } - - - public function testDirectoryCascadingFileLoading() - { - $finder = $this->getFinder(); - $finder->addLocation(__DIR__.'/nested'); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/foo.blade.php')->andReturn(false); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/foo.php')->andReturn(false); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/nested/foo.blade.php')->andReturn(true); - - $this->assertEquals(__DIR__.'/nested/foo.blade.php', $finder->find('foo')); - } - - - public function testNamespacedBasicFileLoading() - { - $finder = $this->getFinder(); - $finder->addNamespace('foo', __DIR__.'/foo'); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/foo/bar/baz.blade.php')->andReturn(true); - - $this->assertEquals(__DIR__.'/foo/bar/baz.blade.php', $finder->find('foo::bar.baz')); - } - - - public function testCascadingNamespacedFileLoading() - { - $finder = $this->getFinder(); - $finder->addNamespace('foo', __DIR__.'/foo'); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/foo/bar/baz.blade.php')->andReturn(false); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/foo/bar/baz.php')->andReturn(true); - - $this->assertEquals(__DIR__.'/foo/bar/baz.php', $finder->find('foo::bar.baz')); - } - - - public function testDirectoryCascadingNamespacedFileLoading() - { - $finder = $this->getFinder(); - $finder->addNamespace('foo', [__DIR__.'/foo', __DIR__.'/bar']); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/foo/bar/baz.blade.php')->andReturn(false); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/foo/bar/baz.php')->andReturn(false); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__.'/bar/bar/baz.blade.php')->andReturn(true); - - $this->assertEquals(__DIR__.'/bar/bar/baz.blade.php', $finder->find('foo::bar.baz')); - } - - - public function testExceptionThrownWhenViewNotFound() - { - $this->expectException(InvalidArgumentException::class); - $finder = $this->getFinder(); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__ . '/foo.blade.php')->andReturn(false); - $finder->getFilesystem()->shouldReceive('exists')->once()->with(__DIR__ . '/foo.php')->andReturn(false); - - $finder->find('foo'); - } - - - public function testExceptionThrownOnInvalidViewName() - { - $this->expectException(InvalidArgumentException::class); - $finder = $this->getFinder(); - $finder->find('name::'); - } - - - public function testExceptionThrownWhenNoHintPathIsRegistered() - { - $this->expectException(InvalidArgumentException::class); - $finder = $this->getFinder(); - $finder->find('name::foo'); - } - - - public function testAddingExtensionPrependsNotAppends() - { - $finder = $this->getFinder(); - $finder->addExtension('baz'); - $extensions = $finder->getExtensions(); - $this->assertEquals('baz', reset($extensions)); - } - - - public function testAddingExtensionsReplacesOldOnes() - { - $finder = $this->getFinder(); - $finder->addExtension('baz'); - $finder->addExtension('baz'); - - $this->assertCount(3, $finder->getExtensions()); - } - - - public function testPassingViewWithHintReturnsTrue() - { - $finder = $this->getFinder(); - - $this->assertTrue($finder->hasHintInformation('hint::foo.bar')); - } - - - public function testPassingViewWithoutHintReturnsFalse() - { - $finder = $this->getFinder(); - - $this->assertFalse($finder->hasHintInformation('foo.bar')); - } - - - public function testPassingViewWithFalseHintReturnsFalse() - { - $finder = $this->getFinder(); - - $this->assertFalse($finder->hasHintInformation('::foo.bar')); - } - - - protected function getFinder() - { - return new Illuminate\View\FileViewFinder(m::mock(Filesystem::class), [__DIR__]); - } - -} diff --git a/tests/View/ViewPhpEngineTest.php b/tests/View/ViewPhpEngineTest.php deleted file mode 100755 index bdcccf7f..00000000 --- a/tests/View/ViewPhpEngineTest.php +++ /dev/null @@ -1,22 +0,0 @@ -assertEquals("Hello World\n", $engine->get(__DIR__ . '/fixtures/basic.php')); - } - -} diff --git a/tests/View/ViewTest.php b/tests/View/ViewTest.php deleted file mode 100755 index b85d7ab3..00000000 --- a/tests/View/ViewTest.php +++ /dev/null @@ -1,232 +0,0 @@ -with('foo', 'bar'); - $view->with(['baz' => 'boom']); - $this->assertEquals(['foo' => 'bar', 'baz' => 'boom'], $view->getData()); - - - $view = new View(m::mock(Factory::class), m::mock( - EngineInterface::class - ), 'view', 'path', [] - ); - $view->withFoo('bar')->withBaz('boom'); - $this->assertEquals(['foo' => 'bar', 'baz' => 'boom'], $view->getData()); - } - - - public function testRenderProperlyRendersView() - { - $view = $this->getView(); - $view->getFactory()->shouldReceive('incrementRender')->once()->ordered(); - $view->getFactory()->shouldReceive('callComposer')->once()->ordered()->with($view); - $view->getFactory()->shouldReceive('getShared')->once()->andReturn(['shared' => 'foo']); - $view->getEngine()->shouldReceive('get')->once()->with('path', ['foo' => 'bar', 'shared' => 'foo'])->andReturn('contents'); - $view->getFactory()->shouldReceive('decrementRender')->once()->ordered(); - $view->getFactory()->shouldReceive('flushStateIfDoneRendering')->once(); - - $me = $this; - $callback = function(View $rendered, $contents) use ($me, $view) - { - $me->assertEquals($view, $rendered); - $me->assertEquals('contents', $contents); - }; - - $this->assertEquals('contents', $view->render($callback)); - } - - - public function testRenderSectionsReturnsEnvironmentSections() - { - $view = m::mock('Illuminate\View\View[render]', [ - m::mock(Factory::class), - m::mock(EngineInterface::class), - 'view', - 'path', - [], - ]); - - $view->shouldReceive('render')->with(m::type('Closure'))->once()->andReturn($sections = ['foo' => 'bar']); - - $this->assertEquals($sections, $view->renderSections()); - } - - - public function testSectionsAreNotFlushedWhenNotDoneRendering() - { - $view = $this->getView(); - $view->getFactory()->shouldReceive('incrementRender')->twice(); - $view->getFactory()->shouldReceive('callComposer')->twice()->with($view); - $view->getFactory()->shouldReceive('getShared')->twice()->andReturn(['shared' => 'foo']); - $view->getEngine()->shouldReceive('get')->twice()->with('path', ['foo' => 'bar', 'shared' => 'foo'])->andReturn('contents'); - $view->getFactory()->shouldReceive('decrementRender')->twice(); - $view->getFactory()->shouldReceive('flushStateIfDoneRendering')->twice(); - - $this->assertEquals('contents', $view->render()); - $this->assertEquals('contents', (string) $view); - } - - - public function testViewNestBindsASubView() - { - $view = $this->getView(); - $view->getFactory()->shouldReceive('make')->once()->with('foo', ['data']); - $result = $view->nest('key', 'foo', ['data']); - - $this->assertInstanceOf(View::class, $result); - } - - - public function testViewAcceptsArrayableImplementations() - { - $arrayable = m::mock(ArrayableInterface::class); - $arrayable->shouldReceive('toArray')->once()->andReturn(['foo' => 'bar', 'baz' => ['qux', 'corge']]); - - $view = new View( - m::mock(Factory::class), - m::mock(EngineInterface::class), - 'view', - 'path', - $arrayable - ); - - $this->assertEquals('bar', $view->foo); - $this->assertEquals(['qux', 'corge'], $view->baz); - } - - - public function testViewGettersSetters() - { - $view = $this->getView(); - $this->assertEquals($view->getName(), 'view'); - $this->assertEquals($view->getPath(), 'path'); - $data = $view->getData(); - $this->assertEquals($data['foo'], 'bar'); - $view->setPath('newPath'); - $this->assertEquals($view->getPath(), 'newPath'); - } - - - public function testViewArrayAccess() - { - $view = $this->getView(); - $this->assertInstanceOf('ArrayAccess', $view); - $this->assertTrue($view->offsetExists('foo')); - $this->assertEquals($view->offsetGet('foo'), 'bar'); - $view->offsetSet('foo','baz'); - $this->assertEquals($view->offsetGet('foo'), 'baz'); - $view->offsetUnset('foo'); - $this->assertFalse($view->offsetExists('foo')); - } - - - public function testViewMagicMethods() - { - $view = $this->getView(); - $this->assertTrue(isset($view->foo)); - $this->assertEquals($view->foo, 'bar'); - $view->foo = 'baz'; - $this->assertEquals($view->foo, 'baz'); - $this->assertEquals($view['foo'], $view->foo); - unset($view->foo); - $this->assertFalse(isset($view->foo)); - $this->assertFalse($view->offsetExists('foo')); - } - - - public function testViewBadMethod() - { - $this->expectException('BadMethodCallException'); - $view = $this->getView(); - $view->badMethodCall(); - } - - - public function testViewGatherDataWithRenderable() - { - $view = $this->getView(); - $view->getFactory()->shouldReceive('incrementRender')->once()->ordered(); - $view->getFactory()->shouldReceive('callComposer')->once()->ordered()->with($view); - $view->getFactory()->shouldReceive('getShared')->once()->andReturn(['shared' => 'foo']); - $view->getEngine()->shouldReceive('get')->once()->andReturn('contents'); - $view->getFactory()->shouldReceive('decrementRender')->once()->ordered(); - $view->getFactory()->shouldReceive('flushStateIfDoneRendering')->once(); - - $view->renderable = m::mock(Renderable::class); - $view->renderable->shouldReceive('render')->once()->andReturn('text'); - $this->assertEquals('contents', $view->render()); - } - - - public function testViewRenderSections() - { - $view = $this->getView(); - $view->getFactory()->shouldReceive('incrementRender')->once()->ordered(); - $view->getFactory()->shouldReceive('callComposer')->once()->ordered()->with($view); - $view->getFactory()->shouldReceive('getShared')->once()->andReturn(['shared' => 'foo']); - $view->getEngine()->shouldReceive('get')->once()->andReturn('contents'); - $view->getFactory()->shouldReceive('decrementRender')->once()->ordered(); - $view->getFactory()->shouldReceive('flushStateIfDoneRendering')->once(); - - $view->getFactory()->shouldReceive('getSections')->once()->andReturn(['foo','bar']); - $sections = $view->renderSections(); - $this->assertEquals($sections[0], 'foo'); - $this->assertEquals($sections[1], 'bar'); - } - - - public function testWithErrors() - { - $view = $this->getView(); - $errors = ['foo' => 'bar', 'qu' => 'ux']; - $this->assertSame($view, $view->withErrors($errors)); - $this->assertInstanceOf(MessageBag::class, $view->errors); - $foo = $view->errors->get('foo'); - $this->assertEquals($foo[0], 'bar'); - $qu = $view->errors->get('qu'); - $this->assertEquals($qu[0], 'ux'); - $data = ['foo' => 'baz']; - $this->assertSame($view, $view->withErrors(new MessageBag($data))); - $foo = $view->errors->get('foo'); - $this->assertEquals($foo[0], 'baz'); - } - - - protected function getView() - { - return new View( - m::mock(Factory::class), - m::mock(EngineInterface::class), - 'view', - 'path', - ['foo' => 'bar'] - ); - } - -} diff --git a/tests/View/fixtures/basic.php b/tests/View/fixtures/basic.php deleted file mode 100755 index 557db03d..00000000 --- a/tests/View/fixtures/basic.php +++ /dev/null @@ -1 +0,0 @@ -Hello World diff --git a/tests/View/fixtures/namespaced/basic.php b/tests/View/fixtures/namespaced/basic.php deleted file mode 100755 index 557db03d..00000000 --- a/tests/View/fixtures/namespaced/basic.php +++ /dev/null @@ -1 +0,0 @@ -Hello World diff --git a/tests/View/fixtures/nested/basic.php b/tests/View/fixtures/nested/basic.php deleted file mode 100755 index 557db03d..00000000 --- a/tests/View/fixtures/nested/basic.php +++ /dev/null @@ -1 +0,0 @@ -Hello World diff --git a/tests/View/fixtures/nested/child.php b/tests/View/fixtures/nested/child.php deleted file mode 100755 index ce0b1ebd..00000000 --- a/tests/View/fixtures/nested/child.php +++ /dev/null @@ -1 +0,0 @@ -Hello World diff --git a/tests/View/fixtures/section-exception-layout.php b/tests/View/fixtures/section-exception-layout.php deleted file mode 100644 index 7b3acd8b..00000000 --- a/tests/View/fixtures/section-exception-layout.php +++ /dev/null @@ -1 +0,0 @@ -yieldContent('content'); ?> diff --git a/tests/View/fixtures/section-exception.php b/tests/View/fixtures/section-exception.php deleted file mode 100644 index 13a8c901..00000000 --- a/tests/View/fixtures/section-exception.php +++ /dev/null @@ -1,4 +0,0 @@ -make('layout', array_except(get_defined_vars(), ['__data', '__path']))->render(); ?> -startSection('content'); ?> - -stopSection(); ?> From 208baf581b82f1a6323ecf26e979c2dbe73ca12d Mon Sep 17 00:00:00 2001 From: agis Date: Tue, 22 Sep 2026 14:20:19 +0700 Subject: [PATCH 25/35] fix(foundation): add Application::terminating() shim for v13 ViewServiceProvider (task 4.3) v13 ServiceProviders (view flushes Blade Component cache) register terminating callbacks; fork Foundation predates the API. Store + run them in terminate(). ponytail shim, remove at task 4.5 foundation swap. --- src/Illuminate/Foundation/Application.php | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index b5f25133..5b5df450 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -61,6 +61,14 @@ class Application extends Container implements HttpKernelInterface, TerminableIn */ protected $finishCallbacks = array(); + /** + * ponytail: v13 terminating-callback shim (v13 ServiceProviders register + * these; fork Foundation predates the API). Remove at task 4.5 foundation swap. + * + * @var array + */ + protected $terminatingCallbacks = array(); + /** * The array of shutdown callbacks. * @@ -603,6 +611,19 @@ public function finish($callback) $this->finishCallbacks[] = $callback; } + /** + * Register a terminating callback (v13 API; see $terminatingCallbacks). + * + * @param callable $callback + * @return $this + */ + public function terminating(callable $callback) + { + $this->terminatingCallbacks[] = $callback; + + return $this; + } + /** * Register a "shutdown" callback. * @@ -868,6 +889,11 @@ public function terminate(SymfonyRequest $request, SymfonyResponse $response): v { $this->callFinishCallbacks($request, $response); + foreach ($this->terminatingCallbacks as $terminating) + { + $this->call($terminating); + } + $this->shutdown(); } From c12c844de0dd4b327ebe7dc3a193fabd9a182ed4 Mon Sep 17 00:00:00 2001 From: agis Date: Tue, 22 Sep 2026 14:37:09 +0700 Subject: [PATCH 26/35] fix(foundation): alias Contracts\View\Factory to 'view' for v13 (task 4.3) v13 view internals (anonymous component rendering) resolve the Factory contract from the container; alias it to the core 'view' binding like the other SCC-1 contract aliases. --- src/Illuminate/Foundation/Application.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 5b5df450..d35ddf42 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -1264,6 +1264,10 @@ public function registerCoreContainerAliases() $this->alias('config', 'Illuminate\Contracts\Config\Repository'); $this->alias('db', 'Illuminate\Database\ConnectionResolverInterface'); + // L13 view swap (task 4.3): v13 view internals (component rendering) resolve + // the Factory contract; alias it to the 'view' binding. + $this->alias('view', 'Illuminate\Contracts\View\Factory'); + // ponytail: v13's Support\Facades\Artisan resolves Illuminate\Contracts\Console\Kernel. // The fork has no console Kernel (task 4.2); 'artisan' is a lazy singleton // (ArtisanServiceProvider), so alias the contract to it globally — works for the From 67c48724dc3dc9c170c295e520575ace23d8a828 Mon Sep 17 00:00:00 2001 From: agis Date: Tue, 22 Sep 2026 14:42:34 +0700 Subject: [PATCH 27/35] fix(foundation): register app as global Container::getInstance() (task 4.3) v13 code resolves the container statically via Container::getInstance() (e.g. BladeCompiler::anonymousComponentPath); the fork bound $this only under the class key, never static setInstance(), so getInstance() returned a bare Container with no bindings/aliases. Call static::setInstance($this) in registerBaseBindings. Fork suite 602 green. --- src/Illuminate/Foundation/Application.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index d35ddf42..12bb47a8 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -147,6 +147,11 @@ protected function registerBaseBindings($request) $this->instance('request', $request); $this->instance('Illuminate\Container\Container', $this); + + // v13 code resolves the container via the static Container::getInstance() + // (e.g. BladeCompiler::anonymousComponentPath); register the app as the + // global instance so those calls hit the real bindings/aliases (task 4.3). + static::setInstance($this); } /** From 684b4b104b6e2f90182418a30761a936abd6cb64 Mon Sep 17 00:00:00 2001 From: agis Date: Tue, 22 Sep 2026 14:44:20 +0700 Subject: [PATCH 28/35] fix(foundation): alias Application/Container contracts to 'app' for v13 (task 4.3) v13 component rendering autowires Contracts\Foundation\Application and the container contracts; mirror v13's 'app' alias cluster. --- src/Illuminate/Foundation/Application.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 12bb47a8..8e07a410 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -1273,6 +1273,12 @@ public function registerCoreContainerAliases() // the Factory contract; alias it to the 'view' binding. $this->alias('view', 'Illuminate\Contracts\View\Factory'); + // v13 component rendering autowires the Application/Container contracts; + // mirror v13's 'app' alias cluster (task 4.3). + $this->alias('app', 'Illuminate\Contracts\Foundation\Application'); + $this->alias('app', 'Illuminate\Contracts\Container\Container'); + $this->alias('app', 'Psr\Container\ContainerInterface'); + // ponytail: v13's Support\Facades\Artisan resolves Illuminate\Contracts\Console\Kernel. // The fork has no console Kernel (task 4.2); 'artisan' is a lazy singleton // (ArtisanServiceProvider), so alias the contract to it globally — works for the From 55bf0d7c353aa1939f6b7c11151132be1703c00d Mon Sep 17 00:00:00 2001 From: agis Date: Tue, 22 Sep 2026 14:45:49 +0700 Subject: [PATCH 29/35] fix(foundation): add getNamespace() shim for v13 ComponentTagCompiler (task 4.3) v13 ComponentTagCompiler calls Application::getNamespace() to locate class components; the app uses only anonymous components so return a benign 'App\' default. ponytail shim, remove at task 4.5. --- src/Illuminate/Foundation/Application.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 8e07a410..0a5602d7 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -616,6 +616,19 @@ public function finish($callback) $this->finishCallbacks[] = $callback; } + /** + * ponytail: v13 Foundation exposes getNamespace() (root PSR-4 namespace) which + * v13's ComponentTagCompiler calls to locate CLASS components. The app uses only + * anonymous components, so the value is never matched — return a benign default + * instead of parsing composer.json. Remove at task 4.5 foundation swap. + * + * @return string + */ + public function getNamespace() + { + return 'App\\'; + } + /** * Register a terminating callback (v13 API; see $terminatingCallbacks). * From 03dee290a134b287260519e64197872ebe901203 Mon Sep 17 00:00:00 2001 From: agis Date: Wed, 23 Sep 2026 13:34:01 +0700 Subject: [PATCH 30/35] feat(console): swap illuminate/console to ^13 (task 4.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - composer: drop illuminate/console from replace, require illuminate/console:^13 (pulls laravel/prompts + nunomaduro/termwind); v13 Console\* shadows fork tree via PSR-4, so the 5 console BC shims go dead (removed at task 4.4/4.5). - Foundation\Artisan::getArtisan() rewired: v13 Console\Application self-bootstraps in its ctor (Container, Dispatcher, version) and has no make()/start()/boot() — so rebind 'artisan' to the console + load start/artisan.php inline (until Foundation Kernel lands at 4.5). - fire() -> handle() on the 22 surviving fork console commands (Foundation/Console 17, Auth/Console 3, Routing/Console 1, Workbench 1) + command.stub; v13 Command::execute() resolves handle() via container, not the old fire() fallback. - tests: fixed 5 fork command tests (setLaravel a Foundation\Application w/ env bound, since v13 Command::run() calls runningUnitTests()); deleted 2 vendored-behavior tests (ConsoleApplicationTest, FoundationArtisanTest — assert old fork Console internals). Fork suite 595 green. Co-Authored-By: Claude Opus 4.8 (1M context) --- composer.json | 2 +- .../Auth/Console/ClearRemindersCommand.php | 2 +- .../Console/RemindersControllerCommand.php | 2 +- .../Auth/Console/RemindersTableCommand.php | 2 +- src/Illuminate/Foundation/Artisan.php | 15 ++- .../Console/AssetPublishCommand.php | 2 +- .../Foundation/Console/AutoloadCommand.php | 2 +- .../Foundation/Console/ChangesCommand.php | 2 +- .../Console/ClearCompiledCommand.php | 2 +- .../Foundation/Console/CommandMakeCommand.php | 2 +- .../Console/ConfigPublishCommand.php | 2 +- .../Foundation/Console/DownCommand.php | 2 +- .../Foundation/Console/EnvironmentCommand.php | 2 +- .../Foundation/Console/KeyGenerateCommand.php | 2 +- .../Console/MigratePublishCommand.php | 2 +- .../Foundation/Console/OptimizeCommand.php | 2 +- .../Foundation/Console/RoutesCommand.php | 2 +- .../Foundation/Console/ServeCommand.php | 2 +- .../Foundation/Console/TinkerCommand.php | 2 +- .../Foundation/Console/UpCommand.php | 2 +- .../Foundation/Console/ViewPublishCommand.php | 2 +- .../Foundation/Console/stubs/command.stub | 2 +- .../Routing/Console/MakeControllerCommand.php | 2 +- .../Console/WorkbenchMakeCommand.php | 2 +- tests/Console/ConsoleApplicationTest.php | 100 ------------------ tests/Foundation/FoundationArtisanTest.php | 42 -------- .../FoundationAssetPublishCommandTest.php | 1 + .../FoundationConfigPublishCommandTest.php | 1 + .../FoundationViewPublishCommandTest.php | 1 + .../RoutingMakeControllerCommandTest.php | 6 +- 30 files changed, 44 insertions(+), 168 deletions(-) delete mode 100755 tests/Console/ConsoleApplicationTest.php delete mode 100755 tests/Foundation/FoundationArtisanTest.php diff --git a/composer.json b/composer.json index 83451dc2..071e1984 100755 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "illuminate/cache": "^13", "illuminate/collections": "^13", "illuminate/conditionable": "^13", + "illuminate/console": "^13", "illuminate/container": "^13", "illuminate/contracts": "^13", "illuminate/cookie": "^13", @@ -59,7 +60,6 @@ "replace": { "illuminate/auth": "self.version", "illuminate/config": "self.version", - "illuminate/console": "self.version", "illuminate/exception": "self.version", "illuminate/foundation": "self.version", "illuminate/hashing": "self.version", diff --git a/src/Illuminate/Auth/Console/ClearRemindersCommand.php b/src/Illuminate/Auth/Console/ClearRemindersCommand.php index 8bb5cb22..3ddf6932 100644 --- a/src/Illuminate/Auth/Console/ClearRemindersCommand.php +++ b/src/Illuminate/Auth/Console/ClearRemindersCommand.php @@ -23,7 +23,7 @@ class ClearRemindersCommand extends Command { * * @return void */ - public function fire() + public function handle() { $this->laravel['auth.reminder.repository']->deleteExpired(); diff --git a/src/Illuminate/Auth/Console/RemindersControllerCommand.php b/src/Illuminate/Auth/Console/RemindersControllerCommand.php index cdc45fc4..4168738b 100644 --- a/src/Illuminate/Auth/Console/RemindersControllerCommand.php +++ b/src/Illuminate/Auth/Console/RemindersControllerCommand.php @@ -45,7 +45,7 @@ public function __construct(Filesystem $files) * * @return void */ - public function fire() + public function handle() { $destination = $this->getPath() . '/RemindersController.php'; diff --git a/src/Illuminate/Auth/Console/RemindersTableCommand.php b/src/Illuminate/Auth/Console/RemindersTableCommand.php index c03801cb..f0d9e8a4 100644 --- a/src/Illuminate/Auth/Console/RemindersTableCommand.php +++ b/src/Illuminate/Auth/Console/RemindersTableCommand.php @@ -44,7 +44,7 @@ public function __construct(Filesystem $files) * * @return void */ - public function fire() + public function handle() { $fullPath = $this->createBaseMigration(); diff --git a/src/Illuminate/Foundation/Artisan.php b/src/Illuminate/Foundation/Artisan.php index 2b6f23da..d1d0ccac 100755 --- a/src/Illuminate/Foundation/Artisan.php +++ b/src/Illuminate/Foundation/Artisan.php @@ -40,9 +40,20 @@ protected function getArtisan() $this->app->loadDeferredProviders(); - $this->artisan = ConsoleApplication::make($this->app); + // v13's Console\Application self-bootstraps in its constructor (dispatches ArtisanStarting + // and runs the starting() callbacks registered by ServiceProvider::commands()). It has no + // make()/start()/boot(). ponytail: the L4.2 static bootstrap — rebinding 'artisan' to the + // console so the Artisan facade in start/artisan.php resolves it, then loading that file — + // is inlined here until the Foundation Kernel lands (task 4.5). + $console = new ConsoleApplication($this->app, $this->app['events'], $this->app::VERSION); - return $this->artisan->boot(); + $this->app->instance('artisan', $console); + + $path = $this->app['path'].'/start/artisan.php'; + + if (file_exists($path)) require $path; + + return $this->artisan = $console; } /** diff --git a/src/Illuminate/Foundation/Console/AssetPublishCommand.php b/src/Illuminate/Foundation/Console/AssetPublishCommand.php index 0b553fff..1898d75c 100755 --- a/src/Illuminate/Foundation/Console/AssetPublishCommand.php +++ b/src/Illuminate/Foundation/Console/AssetPublishCommand.php @@ -47,7 +47,7 @@ public function __construct(AssetPublisher $assets) * * @return int */ - public function fire() + public function handle() { foreach ($this->getPackages() as $package) { diff --git a/src/Illuminate/Foundation/Console/AutoloadCommand.php b/src/Illuminate/Foundation/Console/AutoloadCommand.php index bf5ad7d0..13a50f7b 100755 --- a/src/Illuminate/Foundation/Console/AutoloadCommand.php +++ b/src/Illuminate/Foundation/Console/AutoloadCommand.php @@ -45,7 +45,7 @@ public function __construct(Composer $composer) * * @return int */ - public function fire() + public function handle() { $this->call('optimize'); diff --git a/src/Illuminate/Foundation/Console/ChangesCommand.php b/src/Illuminate/Foundation/Console/ChangesCommand.php index f3cf9788..7622148e 100755 --- a/src/Illuminate/Foundation/Console/ChangesCommand.php +++ b/src/Illuminate/Foundation/Console/ChangesCommand.php @@ -24,7 +24,7 @@ class ChangesCommand extends Command { * * @return int */ - public function fire() + public function handle() { list($version, $changes) = $this->getChangeVersion($this->getChangesArray()); diff --git a/src/Illuminate/Foundation/Console/ClearCompiledCommand.php b/src/Illuminate/Foundation/Console/ClearCompiledCommand.php index be9e7c35..5619be36 100755 --- a/src/Illuminate/Foundation/Console/ClearCompiledCommand.php +++ b/src/Illuminate/Foundation/Console/ClearCompiledCommand.php @@ -23,7 +23,7 @@ class ClearCompiledCommand extends Command { * * @return int */ - public function fire() + public function handle() { if (file_exists($path = $this->laravel['path.base'].'/bootstrap/compiled.php')) { diff --git a/src/Illuminate/Foundation/Console/CommandMakeCommand.php b/src/Illuminate/Foundation/Console/CommandMakeCommand.php index 91e1aed6..c489fc88 100755 --- a/src/Illuminate/Foundation/Console/CommandMakeCommand.php +++ b/src/Illuminate/Foundation/Console/CommandMakeCommand.php @@ -41,7 +41,7 @@ public function __construct(Filesystem $files) * * @return int */ - public function fire() + public function handle() { $path = $this->getPath(); diff --git a/src/Illuminate/Foundation/Console/ConfigPublishCommand.php b/src/Illuminate/Foundation/Console/ConfigPublishCommand.php index 3b968546..46a30c78 100755 --- a/src/Illuminate/Foundation/Console/ConfigPublishCommand.php +++ b/src/Illuminate/Foundation/Console/ConfigPublishCommand.php @@ -49,7 +49,7 @@ public function __construct(ConfigPublisher $config) * * @return int */ - public function fire() + public function handle() { $package = $this->input->getArgument('package'); diff --git a/src/Illuminate/Foundation/Console/DownCommand.php b/src/Illuminate/Foundation/Console/DownCommand.php index 21afba39..d6d57d13 100755 --- a/src/Illuminate/Foundation/Console/DownCommand.php +++ b/src/Illuminate/Foundation/Console/DownCommand.php @@ -23,7 +23,7 @@ class DownCommand extends Command { * * @return int */ - public function fire() + public function handle() { touch($this->laravel['config']['app.manifest'].'/down'); diff --git a/src/Illuminate/Foundation/Console/EnvironmentCommand.php b/src/Illuminate/Foundation/Console/EnvironmentCommand.php index b6036203..cfe9606d 100755 --- a/src/Illuminate/Foundation/Console/EnvironmentCommand.php +++ b/src/Illuminate/Foundation/Console/EnvironmentCommand.php @@ -23,7 +23,7 @@ class EnvironmentCommand extends Command { * * @return int */ - public function fire() + public function handle() { $this->line('Current application environment: '.$this->laravel['env'].''); diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index 32cb73d0..8c749bf7 100755 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -40,7 +40,7 @@ public function __construct(Filesystem $files) * * @return int */ - public function fire() + public function handle() { list($path, $contents) = $this->getKeyFile(); diff --git a/src/Illuminate/Foundation/Console/MigratePublishCommand.php b/src/Illuminate/Foundation/Console/MigratePublishCommand.php index f2720ab7..b248c2c2 100644 --- a/src/Illuminate/Foundation/Console/MigratePublishCommand.php +++ b/src/Illuminate/Foundation/Console/MigratePublishCommand.php @@ -24,7 +24,7 @@ class MigratePublishCommand extends Command { * * @return int */ - public function fire() + public function handle() { $published = $this->laravel['migration.publisher']->publish( $this->getSourcePath(), $this->laravel['path'].'/database/migrations' diff --git a/src/Illuminate/Foundation/Console/OptimizeCommand.php b/src/Illuminate/Foundation/Console/OptimizeCommand.php index c3d4be8e..3d9f5bcd 100644 --- a/src/Illuminate/Foundation/Console/OptimizeCommand.php +++ b/src/Illuminate/Foundation/Console/OptimizeCommand.php @@ -46,7 +46,7 @@ public function __construct(Composer $composer) * * @return int */ - public function fire() + public function handle() { $this->info('Generating optimized class loader'); diff --git a/src/Illuminate/Foundation/Console/RoutesCommand.php b/src/Illuminate/Foundation/Console/RoutesCommand.php index eec91314..3c1603a3 100755 --- a/src/Illuminate/Foundation/Console/RoutesCommand.php +++ b/src/Illuminate/Foundation/Console/RoutesCommand.php @@ -65,7 +65,7 @@ public function __construct(Router $router) * * @return int */ - public function fire() + public function handle() { if (count($this->routes) == 0) { diff --git a/src/Illuminate/Foundation/Console/ServeCommand.php b/src/Illuminate/Foundation/Console/ServeCommand.php index 41c282be..ba514581 100755 --- a/src/Illuminate/Foundation/Console/ServeCommand.php +++ b/src/Illuminate/Foundation/Console/ServeCommand.php @@ -24,7 +24,7 @@ class ServeCommand extends Command { * * @return int */ - public function fire() + public function handle() { $this->checkPhpVersion(); diff --git a/src/Illuminate/Foundation/Console/TinkerCommand.php b/src/Illuminate/Foundation/Console/TinkerCommand.php index 3e7b41a8..f55da969 100755 --- a/src/Illuminate/Foundation/Console/TinkerCommand.php +++ b/src/Illuminate/Foundation/Console/TinkerCommand.php @@ -24,7 +24,7 @@ class TinkerCommand extends Command { * * @return int */ - public function fire() + public function handle() { if ($this->supportsBoris()) { diff --git a/src/Illuminate/Foundation/Console/UpCommand.php b/src/Illuminate/Foundation/Console/UpCommand.php index 7787994b..0c565d60 100755 --- a/src/Illuminate/Foundation/Console/UpCommand.php +++ b/src/Illuminate/Foundation/Console/UpCommand.php @@ -23,7 +23,7 @@ class UpCommand extends Command { * * @return int */ - public function fire() + public function handle() { @unlink($this->laravel['config']['app.manifest'].'/down'); diff --git a/src/Illuminate/Foundation/Console/ViewPublishCommand.php b/src/Illuminate/Foundation/Console/ViewPublishCommand.php index 787b3132..7e939c27 100755 --- a/src/Illuminate/Foundation/Console/ViewPublishCommand.php +++ b/src/Illuminate/Foundation/Console/ViewPublishCommand.php @@ -46,7 +46,7 @@ public function __construct(ViewPublisher $view) * * @return int */ - public function fire() + public function handle() { $package = $this->input->getArgument('package'); diff --git a/src/Illuminate/Foundation/Console/stubs/command.stub b/src/Illuminate/Foundation/Console/stubs/command.stub index d48e6753..6a98d455 100755 --- a/src/Illuminate/Foundation/Console/stubs/command.stub +++ b/src/Illuminate/Foundation/Console/stubs/command.stub @@ -35,7 +35,7 @@ class {{class}} extends Command { * * @return mixed */ - public function fire() + public function handle() { // } diff --git a/src/Illuminate/Routing/Console/MakeControllerCommand.php b/src/Illuminate/Routing/Console/MakeControllerCommand.php index 5eeb8e96..594c60b5 100755 --- a/src/Illuminate/Routing/Console/MakeControllerCommand.php +++ b/src/Illuminate/Routing/Console/MakeControllerCommand.php @@ -55,7 +55,7 @@ public function __construct(ControllerGenerator $generator, $path) * * @return int */ - public function fire() + public function handle() { $this->generateController(); diff --git a/src/Illuminate/Workbench/Console/WorkbenchMakeCommand.php b/src/Illuminate/Workbench/Console/WorkbenchMakeCommand.php index 5a32cb98..37295961 100755 --- a/src/Illuminate/Workbench/Console/WorkbenchMakeCommand.php +++ b/src/Illuminate/Workbench/Console/WorkbenchMakeCommand.php @@ -47,7 +47,7 @@ public function __construct(PackageCreator $creator) * * @return void */ - public function fire() + public function handle() { $workbench = $this->runCreator($this->buildPackage()); diff --git a/tests/Console/ConsoleApplicationTest.php b/tests/Console/ConsoleApplicationTest.php deleted file mode 100755 index 6a5813ad..00000000 --- a/tests/Console/ConsoleApplicationTest.php +++ /dev/null @@ -1,100 +0,0 @@ -getMock(\Illuminate\Console\Application::class, ['addToParent']); - $app->setLaravel('foo'); - $command = m::mock(\Illuminate\Console\Command::class); - $command->shouldReceive('setLaravel')->once()->with('foo'); - $app->expects($this->once())->method('addToParent')->with($this->equalTo($command))->willReturn($command); - $result = $app->add($command); - - $this->assertEquals($command, $result); - } - - - public function testLaravelNotSetOnSymfonyCommands() - { - $app = $this->getMock(\Illuminate\Console\Application::class, ['addToParent']); - $app->setLaravel('foo'); - $command = m::mock(Command::class); - $command->shouldReceive('setLaravel')->never(); - $app->expects($this->once())->method('addToParent')->with($this->equalTo($command))->willReturn($command); - $result = $app->add($command); - - $this->assertEquals($command, $result); - } - - - public function testResolveAddsCommandViaApplicationResolution() - { - $app = $this->getMock(\Illuminate\Console\Application::class, ['addToParent']); - $command = m::mock(Command::class); - $app->setLaravel(['foo' => $command]); - $app->expects($this->once())->method('addToParent')->with($this->equalTo($command))->willReturn($command); - $result = $app->resolve('foo'); - - $this->assertEquals($command, $result); - } - - - public function testResolveCommandsCallsResolveForAllCommandsItsGiven() - { - $app = m::mock('Illuminate\Console\Application[resolve]'); - $app->shouldReceive('resolve')->twice()->with('foo'); - $app->resolveCommands('foo', 'foo'); - } - - - public function testResolveCommandsCallsResolveForAllCommandsItsGivenViaArray() - { - $app = m::mock('Illuminate\Console\Application[resolve]'); - $app->shouldReceive('resolve')->twice()->with('foo'); - $app->resolveCommands(['foo', 'foo']); - } - - - public function testExecuteResolvesHandleThenFallsBackToFire() - { - $execute = new \ReflectionMethod(\Illuminate\Console\Command::class, 'execute'); - $execute->setAccessible(true); - $input = new ArrayInput([]); - $output = new NullOutput; - - // handle() is preferred (L13 idiom) - $this->assertSame(0, $execute->invoke(new ConsoleHandleStub, $input, $output)); - $this->assertEquals('handle', $_SERVER['__console.ran']); - - // fire() still runs as the L4.2 fallback when no handle() exists - $execute->invoke(new ConsoleFireStub, $input, $output); - $this->assertEquals('fire', $_SERVER['__console.ran']); - } - -} - -class ConsoleHandleStub extends \Illuminate\Console\Command -{ - protected $name = 'stub:handle'; - public function handle() { $_SERVER['__console.ran'] = 'handle'; } -} - -class ConsoleFireStub extends \Illuminate\Console\Command -{ - protected $name = 'stub:fire'; - public function fire() { $_SERVER['__console.ran'] = 'fire'; } -} diff --git a/tests/Foundation/FoundationArtisanTest.php b/tests/Foundation/FoundationArtisanTest.php deleted file mode 100755 index 573e377b..00000000 --- a/tests/Foundation/FoundationArtisanTest.php +++ /dev/null @@ -1,42 +0,0 @@ -getMock( - Artisan::class, - ['getArtisan'], - [$app = new Illuminate\Foundation\Application] - ); - $artisan->expects($this->once())->method('getArtisan')->willReturn( - $console = m::mock('Illuminate\Console\Application[find]') - ); - $console->shouldReceive('find')->once()->with('foo')->andReturn($command = m::mock(\Symfony\Component\Console\Command\Command::class)); - $command->shouldReceive('run')->once()->with(m::type(ArrayInput::class), m::type( - NullOutput::class - ))->andReturnUsing(function($input, $output) use (&$captured) - { - $captured = $input; - - return 0; - }); - - $artisan->call('foo', ['--bar' => 'baz']); - $this->assertEquals('baz', $captured->getParameterOption('--bar')); - } - -} diff --git a/tests/Foundation/FoundationAssetPublishCommandTest.php b/tests/Foundation/FoundationAssetPublishCommandTest.php index 54ed6b32..26978592 100755 --- a/tests/Foundation/FoundationAssetPublishCommandTest.php +++ b/tests/Foundation/FoundationAssetPublishCommandTest.php @@ -19,6 +19,7 @@ public function testCommandCallsPublisherWithProperPackageName() $pub = m::mock(AssetPublisher::class) ); $pub->shouldReceive('publishPackage')->once()->with('foo'); + $command->setLaravel(tap(new Illuminate\Foundation\Application, fn($a) => $a->instance('env', 'testing'))); $command->run( new Symfony\Component\Console\Input\ArrayInput(['package' => 'foo']), new Symfony\Component\Console\Output\NullOutput diff --git a/tests/Foundation/FoundationConfigPublishCommandTest.php b/tests/Foundation/FoundationConfigPublishCommandTest.php index 5a4f022f..2d6e7515 100755 --- a/tests/Foundation/FoundationConfigPublishCommandTest.php +++ b/tests/Foundation/FoundationConfigPublishCommandTest.php @@ -20,6 +20,7 @@ public function testCommandCallsPublisherWithProperPackageName() ); $pub->shouldReceive('alreadyPublished')->andReturn(false); $pub->shouldReceive('publishPackage')->once()->with('foo'); + $command->setLaravel(tap(new Illuminate\Foundation\Application, fn($a) => $a->instance('env', 'testing'))); $command->run(new Symfony\Component\Console\Input\ArrayInput(['package' => 'foo']), new Symfony\Component\Console\Output\NullOutput); } diff --git a/tests/Foundation/FoundationViewPublishCommandTest.php b/tests/Foundation/FoundationViewPublishCommandTest.php index 80686136..7c5ec833 100755 --- a/tests/Foundation/FoundationViewPublishCommandTest.php +++ b/tests/Foundation/FoundationViewPublishCommandTest.php @@ -19,6 +19,7 @@ public function testCommandCallsPublisherWithProperPackageName() $pub = m::mock(ViewPublisher::class) ); $pub->shouldReceive('publishPackage')->once()->with('foo'); + $command->setLaravel(tap(new Illuminate\Foundation\Application, fn($a) => $a->instance('env', 'testing'))); $command->run( new Symfony\Component\Console\Input\ArrayInput(['package' => 'foo']), new Symfony\Component\Console\Output\NullOutput diff --git a/tests/Routing/RoutingMakeControllerCommandTest.php b/tests/Routing/RoutingMakeControllerCommandTest.php index 6876c79d..8fa9f9b0 100755 --- a/tests/Routing/RoutingMakeControllerCommandTest.php +++ b/tests/Routing/RoutingMakeControllerCommandTest.php @@ -23,6 +23,7 @@ public function testGeneratorIsCalledWithProperOptions() __DIR__, ['only' => [], 'except' => []] ); + $command->setLaravel(tap(new Illuminate\Foundation\Application, fn($a) => $a->instance('env', 'testing'))); $this->runCommand($command, ['name' => 'FooController']); } @@ -34,7 +35,10 @@ public function testGeneratorIsCalledWithProperOptionsForExceptAndOnly() ), __DIR__); $gen->shouldReceive('make')->once()->with('FooController', __DIR__.'/foo/bar', ['only' => ['foo', 'bar'], 'except' => ['baz', 'boom']] ); - $command->setLaravel(['path.base' => __DIR__.'/foo']); + $laravel = new Illuminate\Foundation\Application; + $laravel->instance('path.base', __DIR__.'/foo'); + $laravel->instance('env', 'testing'); + $command->setLaravel($laravel); $this->runCommand($command, ['name' => 'FooController', '--only' => 'foo,bar', '--except' => 'baz,boom', '--path' => 'bar'] ); } From a9bb6b1418db1a9ca915b62b1174950b81396f26 Mon Sep 17 00:00:00 2001 From: agis Date: Wed, 23 Sep 2026 13:50:14 +0700 Subject: [PATCH 31/35] fix(console): wire artisan boot for v13 Console\Application (task 4.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Surfaced by app-side artisan boot-smoke (fork suite couldn't catch — no app): - Foundation\Artisan::getArtisan(): call $app->boot() before building the console (the old Console\Application::make() did; provider boot()s register runtime extensions like the app's Auth::extend('dicoding-auth') that command resolution needs), and setContainerCommandLoader() after (v13 registers #[AsCommand] commands lazily into a commandMap — without the loader, illuminate/database's migrate family is invisible). - Foundation\Application::basePath(): v13 database MigrationServiceProvider calls it. - 'composer' binding -> illuminate/support Composer (v13 moved it there; MigrateMakeCommand type-hints Support\Composer); fork Optimize/AutoloadCommand retyped to match. artisan list now clean: 191 commands incl. full migrate family + queue:work. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Foundation/Application.php | 14 ++++++++++++++ src/Illuminate/Foundation/Artisan.php | 11 +++++++++++ .../Foundation/Console/AutoloadCommand.php | 2 +- .../Foundation/Console/OptimizeCommand.php | 2 +- .../Providers/ComposerServiceProvider.php | 2 +- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 0a5602d7..22b58b1a 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -230,6 +230,20 @@ public function resourcePath($path = '') return $this['path.base'].DIRECTORY_SEPARATOR.'resources'.($path != '' ? DIRECTORY_SEPARATOR.$path : ''); } + /** + * Get the base path of the installation. + * + * ponytail: v13 ServiceProviders (e.g. database's MigrationServiceProvider) call basePath(); + * the L4.2 fork Application lacks it. Remove once Foundation swaps to v13. + * + * @param string $path + * @return string + */ + public function basePath($path = '') + { + return $this['path.base'].($path != '' ? DIRECTORY_SEPARATOR.$path : ''); + } + /** * Get the application bootstrap file. * diff --git a/src/Illuminate/Foundation/Artisan.php b/src/Illuminate/Foundation/Artisan.php index d1d0ccac..c1e76b73 100755 --- a/src/Illuminate/Foundation/Artisan.php +++ b/src/Illuminate/Foundation/Artisan.php @@ -40,6 +40,11 @@ protected function getArtisan() $this->app->loadDeferredProviders(); + // Boot the app before building the console (the old Console\Application::make() did this): + // provider boot()s register runtime extensions — e.g. the app's custom auth driver via + // Auth::extend — that command resolution depends on. + $this->app->boot(); + // v13's Console\Application self-bootstraps in its constructor (dispatches ArtisanStarting // and runs the starting() callbacks registered by ServiceProvider::commands()). It has no // make()/start()/boot(). ponytail: the L4.2 static bootstrap — rebinding 'artisan' to the @@ -53,6 +58,12 @@ protected function getArtisan() if (file_exists($path)) require $path; + // v13 registers attribute-named commands (#[AsCommand]) lazily into a commandMap; they + // only become resolvable once the container command loader is attached (the v13 Kernel + // does the same after resolving). Without this, e.g. illuminate/database's migrate stays + // invisible. Called last, so the map is complete (ctor bootstrappers + start/artisan.php). + $console->setContainerCommandLoader(); + return $this->artisan = $console; } diff --git a/src/Illuminate/Foundation/Console/AutoloadCommand.php b/src/Illuminate/Foundation/Console/AutoloadCommand.php index 13a50f7b..c009ae22 100755 --- a/src/Illuminate/Foundation/Console/AutoloadCommand.php +++ b/src/Illuminate/Foundation/Console/AutoloadCommand.php @@ -1,7 +1,7 @@ Date: Wed, 23 Sep 2026 14:00:01 +0700 Subject: [PATCH 32/35] fix(console): fork OptimizeCommand drop Process getCommandLine() call (task 4.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v13 Support\Composer::dumpAutoloads()/dumpOptimized() return an int exit code, not a Process — the fork command only used the result for a cosmetic 'Executed: ...' info line. Fixes fatal in 'artisan optimize' (composer post-update hook). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Foundation/Console/OptimizeCommand.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Illuminate/Foundation/Console/OptimizeCommand.php b/src/Illuminate/Foundation/Console/OptimizeCommand.php index 20aeb9fa..2cbdbdac 100644 --- a/src/Illuminate/Foundation/Console/OptimizeCommand.php +++ b/src/Illuminate/Foundation/Console/OptimizeCommand.php @@ -52,19 +52,17 @@ public function handle() if ($this->option('psr')) { - $process = $this->composer->dumpAutoloads(); + $this->composer->dumpAutoloads(); } elseif ($this->option('apcu')) { - $process = $this->composer->dumpAutoloads('--optimize --apcu'); + $this->composer->dumpAutoloads('--optimize --apcu'); } else { - $process = $this->composer->dumpOptimized(); + $this->composer->dumpOptimized(); } - $this->info("Executed: {$process->getCommandLine()}"); - if ($this->option('force') || ! $this->laravel['config']['app.debug']) { $this->info('Compiling views'); From 8702eb78b797f74e7d86d5de5fe16b1725135fcc Mon Sep 17 00:00:00 2001 From: agis Date: Wed, 23 Sep 2026 16:39:23 +0700 Subject: [PATCH 33/35] fix(foundation): guard deferred-provider boot() for v13 providers (register path) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Application::register() booted a provider registered after boot via a bare `$provider->boot()` (line 378), unguarded. v13 ServiceProviders have no default boot(), so a DEFERRED provider resolved post-boot (e.g. Illuminate\Redis\ RedisServiceProvider, triggered whenever redis is resolved — oauth token store, cache) fatals: "Call to undefined method RedisServiceProvider::boot()", surfacing as a silent Symfony 500 (no app log). Mirror boot()'s guard: method_exists + container-call. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Foundation/Application.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Application.php b/src/Illuminate/Foundation/Application.php index 22b58b1a..c3fa38fb 100755 --- a/src/Illuminate/Foundation/Application.php +++ b/src/Illuminate/Foundation/Application.php @@ -375,7 +375,10 @@ public function register($provider, $options = array(), $force = false) // If the application has already booted, we will call this boot method on // the provider class so it has an opportunity to do its boot logic and // will be ready for any usage by the developer's application logics. - if ($this->booted) $provider->boot(); + // v13 ServiceProvider has no default boot(); guard + container-call to + // mirror boot() so deferred providers (e.g. RedisServiceProvider) resolved + // after boot don't fatal on a missing boot() method. + if ($this->booted && method_exists($provider, 'boot')) $this->call([$provider, 'boot']); return $provider; } From 1d35c43d656158311e5020f0c06afef03cf3fd01 Mon Sep 17 00:00:00 2001 From: agis Date: Sat, 26 Sep 2026 10:05:20 +0700 Subject: [PATCH 34/35] fix(queue): make console commands v13-console compatible (task 4.2) v13 illuminate/console Command::execute() resolves handle() or __invoke(), dropping L4.2's fire(). The fork queue console commands (still self.version) kept fire(), so v13 fell back to __invoke() -> BoundMethod "__invoke() does not exist" on queue:listen/work/retry/etc. Rename fire()->handle() to match the v13 idiom the app-side sweep already applied to app commands. Also drop ListFailedCommand's removed-Symfony 'table' HelperSet call in favour of the v13 Command::table() helper (the old path only surfaced now that the command actually reaches its body). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Queue/Console/FailedTableCommand.php | 2 +- src/Illuminate/Queue/Console/FlushFailedCommand.php | 2 +- src/Illuminate/Queue/Console/ForgetFailedCommand.php | 2 +- src/Illuminate/Queue/Console/ListFailedCommand.php | 8 ++------ src/Illuminate/Queue/Console/ListenCommand.php | 2 +- src/Illuminate/Queue/Console/RestartCommand.php | 2 +- src/Illuminate/Queue/Console/RetryCommand.php | 2 +- src/Illuminate/Queue/Console/SubscribeCommand.php | 2 +- src/Illuminate/Queue/Console/WorkCommand.php | 2 +- 9 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Illuminate/Queue/Console/FailedTableCommand.php b/src/Illuminate/Queue/Console/FailedTableCommand.php index 6cbf8632..521002f1 100644 --- a/src/Illuminate/Queue/Console/FailedTableCommand.php +++ b/src/Illuminate/Queue/Console/FailedTableCommand.php @@ -44,7 +44,7 @@ public function __construct(Filesystem $files) * * @return int */ - public function fire() + public function handle() { $fullPath = $this->createBaseMigration(); diff --git a/src/Illuminate/Queue/Console/FlushFailedCommand.php b/src/Illuminate/Queue/Console/FlushFailedCommand.php index 91f6fba9..34e10514 100644 --- a/src/Illuminate/Queue/Console/FlushFailedCommand.php +++ b/src/Illuminate/Queue/Console/FlushFailedCommand.php @@ -22,7 +22,7 @@ class FlushFailedCommand extends Command { * Execute the console command. * */ - public function fire() + public function handle() { $this->laravel['queue.failer']->flush(); diff --git a/src/Illuminate/Queue/Console/ForgetFailedCommand.php b/src/Illuminate/Queue/Console/ForgetFailedCommand.php index 6083ca60..2bd9ad8e 100644 --- a/src/Illuminate/Queue/Console/ForgetFailedCommand.php +++ b/src/Illuminate/Queue/Console/ForgetFailedCommand.php @@ -24,7 +24,7 @@ class ForgetFailedCommand extends Command { * * @return int */ - public function fire() + public function handle() { if ($this->laravel['queue.failer']->forget($this->argument('id'))) { diff --git a/src/Illuminate/Queue/Console/ListFailedCommand.php b/src/Illuminate/Queue/Console/ListFailedCommand.php index 328c97b9..2a539fae 100644 --- a/src/Illuminate/Queue/Console/ListFailedCommand.php +++ b/src/Illuminate/Queue/Console/ListFailedCommand.php @@ -23,7 +23,7 @@ class ListFailedCommand extends Command { * * @return int */ - public function fire() + public function handle() { $rows = array(); @@ -38,11 +38,7 @@ public function fire() return 0; } - $table = $this->getHelperSet()->get('table'); - - $table->setHeaders(array('ID', 'Connection', 'Queue', 'Class', 'Failed At')) - ->setRows($rows) - ->render($this->output); + $this->table(array('ID', 'Connection', 'Queue', 'Class', 'Failed At'), $rows); return 0; } diff --git a/src/Illuminate/Queue/Console/ListenCommand.php b/src/Illuminate/Queue/Console/ListenCommand.php index a07c720d..b7fed568 100755 --- a/src/Illuminate/Queue/Console/ListenCommand.php +++ b/src/Illuminate/Queue/Console/ListenCommand.php @@ -46,7 +46,7 @@ public function __construct(Listener $listener) * * @return int */ - public function fire() + public function handle() { $this->setListenerOptions(); diff --git a/src/Illuminate/Queue/Console/RestartCommand.php b/src/Illuminate/Queue/Console/RestartCommand.php index 80313e09..6ad6f9b7 100644 --- a/src/Illuminate/Queue/Console/RestartCommand.php +++ b/src/Illuminate/Queue/Console/RestartCommand.php @@ -23,7 +23,7 @@ class RestartCommand extends Command { * * @return int */ - public function fire() + public function handle() { $this->laravel['cache']->forever('illuminate:queue:restart', time()); diff --git a/src/Illuminate/Queue/Console/RetryCommand.php b/src/Illuminate/Queue/Console/RetryCommand.php index d7595397..293dec0a 100644 --- a/src/Illuminate/Queue/Console/RetryCommand.php +++ b/src/Illuminate/Queue/Console/RetryCommand.php @@ -24,7 +24,7 @@ class RetryCommand extends Command { * * @return int */ - public function fire() + public function handle() { $failed = $this->laravel['queue.failer']->find($this->argument('id')); diff --git a/src/Illuminate/Queue/Console/SubscribeCommand.php b/src/Illuminate/Queue/Console/SubscribeCommand.php index c0b6c3e6..7c34d519 100755 --- a/src/Illuminate/Queue/Console/SubscribeCommand.php +++ b/src/Illuminate/Queue/Console/SubscribeCommand.php @@ -36,7 +36,7 @@ class SubscribeCommand extends Command { * * @throws \RuntimeException */ - public function fire() + public function handle() { $iron = $this->laravel['queue']->connection(); diff --git a/src/Illuminate/Queue/Console/WorkCommand.php b/src/Illuminate/Queue/Console/WorkCommand.php index 8d39b230..2aca6dbe 100755 --- a/src/Illuminate/Queue/Console/WorkCommand.php +++ b/src/Illuminate/Queue/Console/WorkCommand.php @@ -47,7 +47,7 @@ public function __construct(Worker $worker) * * @return int */ - public function fire() + public function handle() { if ($this->downForMaintenance() && ! $this->option('daemon')) return 0; From 015f5efe3f308f0c4d8e7cb77af8ca3a361b19e8 Mon Sep 17 00:00:00 2001 From: agis Date: Sat, 26 Sep 2026 10:37:16 +0700 Subject: [PATCH 35/35] fix(foundation): memoize Artisan console before requiring artisan.php (task 4.2) getArtisan() binds the v13 console as 'artisan' then require's start/artisan.php, which calls Artisan::add() ~149x. The Artisan facade has already cached this Foundation\Artisan wrapper, so each add() re-enters __call()->getArtisan(); with $this->artisan memoized only at the end, every re-entry re-boots and re-requires artisan.php -> unbounded recursion -> OOM (observed ~22GB) on any Artisan::call in tests/runtime. Memoize the console before the require so re-entry short-circuits and add() forwards to the console instance. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/Illuminate/Foundation/Artisan.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Illuminate/Foundation/Artisan.php b/src/Illuminate/Foundation/Artisan.php index c1e76b73..317af495 100755 --- a/src/Illuminate/Foundation/Artisan.php +++ b/src/Illuminate/Foundation/Artisan.php @@ -54,6 +54,12 @@ protected function getArtisan() $this->app->instance('artisan', $console); + // Memoize before requiring start/artisan.php: that file calls Artisan::add() ~149x, and the + // Artisan facade has already cached THIS wrapper, so each add() re-enters __call()->getArtisan(). + // Without the early memo it re-boots + re-requires artisan.php recursively (OOM). With it, the + // re-entrant getArtisan() short-circuits and add() forwards to the console instance. + $this->artisan = $console; + $path = $this->app['path'].'/start/artisan.php'; if (file_exists($path)) require $path;