Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/Illuminate/Collections/Arr.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,13 @@ public static function first($array, ?callable $callback = null, $default = null
return value($default);
}

$key = array_find_key($array, $callback);
foreach ($array as $key => $item) {
if ($callback($item, $key)) {
return $item;
}
}

return $key !== null ? $array[$key] : value($default);
return value($default);
}

/**
Expand All @@ -287,7 +291,7 @@ public static function first($array, ?callable $callback = null, $default = null
public static function last($array, ?callable $callback = null, $default = null)
{
if (is_null($callback)) {
return empty($array) ? value($default) : array_last($array);
return empty($array) ? value($default) : end($array);
}

return static::first(array_reverse($array, true), $callback, $default);
Expand Down
16 changes: 14 additions & 2 deletions src/Illuminate/Collections/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,13 @@ public function contains($key, $operator = null, $value = null)
{
if (func_num_args() === 1) {
if ($this->useAsCallable($key)) {
return array_any($this->items, $key);
foreach ($this->items as $item) {
if ($key($item)) {
return true;
}
}

return false;
}

return in_array($key, $this->items);
Expand Down Expand Up @@ -617,7 +623,13 @@ public function hasAny($key)

$keys = is_array($key) ? $key : func_get_args();

return array_any($keys, fn ($key) => array_key_exists($key, $this->items));
foreach ($keys as $key) {
if (array_key_exists($key, $this->items)) {
return true;
}
}

return false;
}

/**
Expand Down
40 changes: 31 additions & 9 deletions src/Illuminate/Concurrency/ProcessDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,54 @@ public function __construct(protected ProcessFactory $processFactory)
/**
* Run the given tasks concurrently and return an array containing the results.
*/
public function run(Closure|array $tasks): array
public function run(Closure|array $tasks, ?int $timeout = null): array
{
$command = Application::formatCommandString('invoke-serialized-closure');

$results = $this->processFactory->pool(function (Pool $pool) use ($tasks, $command) {
$results = $this->processFactory->pool(function (Pool $pool) use ($tasks, $command, $timeout) {
foreach (Arr::wrap($tasks) as $key => $task) {
$pool->as($key)->path(base_path())->env([
$process = $pool->as($key)->path(base_path())->env([
'LARAVEL_INVOKABLE_CLOSURE' => base64_encode(
serialize(new SerializableClosure($task))
),
])->command($command);

if ($timeout !== null) {
$process->timeout($timeout);
}
}
})->start()->wait();

return $results->collect()->mapWithKeys(function ($result, $key) {
if ($result->failed()) {
throw new Exception('Concurrent process failed with exit code ['.$result->exitCode().']. Message: '.$result->errorOutput());
$errorMessage = $result->errorOutput() ?: 'Process failed with no error output';
throw new Exception("Concurrent process [{$key}] failed with exit code [{$result->exitCode()}]. Error: {$errorMessage}");
}

$output = $result->output();
if (empty($output)) {
throw new Exception("Concurrent process [{$key}] produced no output");
}

$result = json_decode($result->output(), true);
$result = json_decode($output, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception("Concurrent process [{$key}] produced invalid JSON output: ".json_last_error_msg());
}

if (! $result['successful']) {
throw new $result['exception'](
...(! empty(array_filter($result['parameters']))
? $result['parameters']
: [$result['message']])
$exceptionClass = $result['exception'] ?? Exception::class;
$message = $result['message'] ?? 'Unknown error occurred';
$parameters = $result['parameters'] ?? [];

// Ensure exception class exists
if (! class_exists($exceptionClass)) {
throw new Exception("Process [{$key}] failed with unknown exception class: {$exceptionClass}. Message: {$message}");
}

throw new $exceptionClass(
...(! empty(array_filter($parameters))
? $parameters
: [$message])
);
}

Expand Down
10 changes: 7 additions & 3 deletions src/Illuminate/Concurrency/SyncDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ class SyncDriver implements Driver
*/
public function run(Closure|array $tasks): array
{
return Collection::wrap($tasks)->map(
fn ($task) => $task()
)->all();
return Collection::wrap($tasks)->map(function ($task, $key) {
try {
return $task();
} catch (\Throwable $e) {
throw new \Exception("Synchronous task [{$key}] failed: ".$e->getMessage(), 0, $e);
}
})->all();
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/Illuminate/Container/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Illuminate\Contracts\Container\Container as ContainerContract;
use Illuminate\Contracts\Container\ContextualAttribute;
use Illuminate\Contracts\Container\SelfBuilding;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use LogicException;
use ReflectionAttribute;
Expand Down Expand Up @@ -1320,7 +1321,7 @@ protected function getParameterOverride($dependency)
*/
protected function getLastParameterOverride()
{
return count($this->with) ? array_last($this->with) : [];
return count($this->with) ? Arr::last($this->with) : [];
}

/**
Expand Down Expand Up @@ -1680,7 +1681,7 @@ protected function fireCallbackArray($object, array $callbacks)
*/
public function currentlyResolving()
{
return array_last($this->buildStack) ?: null;
return Arr::last($this->buildStack) ?: null;
}

/**
Expand Down
34 changes: 34 additions & 0 deletions src/Illuminate/Database/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ class Connection implements ConnectionInterface
*/
protected $queryLog = [];

/**
* The maximum number of queries to keep in the query log.
*
* @var int
*/
protected $maxQueryLogSize = 1000;

/**
* Indicates whether queries are being logged.
*
Expand Down Expand Up @@ -858,6 +865,10 @@ public function logQuery($query, $bindings, $time = null)

if ($this->loggingQueries) {
$this->queryLog[] = compact('query', 'bindings', 'time');

if (count($this->queryLog) > $this->maxQueryLogSize) {
$this->queryLog = array_slice($this->queryLog, -$this->maxQueryLogSize, $this->maxQueryLogSize, false);
}
}
}

Expand Down Expand Up @@ -1579,6 +1590,29 @@ public function logging()
return $this->loggingQueries;
}

/**
* Set the maximum query log size.
*
* @param int $size
* @return $this
*/
public function setMaxQueryLogSize(int $size)
{
$this->maxQueryLogSize = max(1, $size);

return $this;
}

/**
* Get the maximum query log size.
*
* @return int
*/
public function getMaxQueryLogSize()
{
return $this->maxQueryLogSize;
}

/**
* Get the name of the connected database.
*
Expand Down
26 changes: 22 additions & 4 deletions src/Illuminate/Foundation/Bootstrap/HandleExceptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -330,12 +330,30 @@ public static function flushState(?TestCase $testCase = null)
*/
public static function flushHandlersState(?TestCase $testCase = null)
{
while (get_exception_handler() !== null) {
restore_exception_handler();
if (function_exists('get_exception_handler')) {
while (get_exception_handler() !== null) {
restore_exception_handler();
}
} else {
// Fallback for PHP < 8.4
$handler = set_exception_handler(null);
if ($handler !== null) {
set_exception_handler($handler);
restore_exception_handler();
}
}

while (get_error_handler() !== null) {
restore_error_handler();
if (function_exists('get_error_handler')) {
while (get_error_handler() !== null) {
restore_error_handler();
}
} else {
// Fallback for PHP < 8.4
$handler = set_error_handler(null);
if ($handler !== null) {
set_error_handler($handler);
restore_error_handler();
}
}

if (class_exists(ErrorHandler::class)) {
Expand Down
3 changes: 2 additions & 1 deletion src/Illuminate/Testing/PendingCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,10 @@ public function expectsPromptsOutro(string $message)
*
* @param array<int, string|array<int, string>>|Collection<int, string|array<int, string>> $headers
* @param array<int, array<int, string>>|Collection<int, array<int, string>>|null $rows
* @return $this
*
* @phpstan-param ($rows is null ? list<list<string>>|Collection<int, list<string>> : list<string|list<string>>|Collection<int, string|list<string>>) $headers
*
* @return $this
*/
public function expectsPromptsTable(array|Collection $headers, array|Collection|null $rows)
{
Expand Down
3 changes: 2 additions & 1 deletion src/Illuminate/Validation/InvokableValidationRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ protected function __construct(ValidationRule|InvokableRule $invokable)
public static function make($invokable)
{
if ($invokable->implicit ?? false) {
return new class($invokable) extends InvokableValidationRule implements ImplicitRule {
return new class($invokable) extends InvokableValidationRule implements ImplicitRule
{
};
}

Expand Down
9 changes: 6 additions & 3 deletions tests/Bus/BusPendingBatchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ public function test_batch_is_deleted_from_storage_if_exception_thrown_during_ba

$container = new Container;

$job = new class {
$job = new class
{
};

$pendingBatch = new PendingBatch($container, new Collection([$job]));
Expand Down Expand Up @@ -226,7 +227,8 @@ public function test_batch_before_event_is_called()

public function test_it_throws_exception_if_batched_job_is_not_batchable(): void
{
$nonBatchableJob = new class {
$nonBatchableJob = new class
{
};

$this->expectException(RuntimeException::class);
Expand All @@ -242,7 +244,8 @@ public function test_it_throws_an_exception_if_batched_job_contains_batch_with_n
new PendingBatch(
$container,
new Collection(
[new PendingBatch($container, new Collection([new BatchableJob, new class {
[new PendingBatch($container, new Collection([new BatchableJob, new class
{
}]))]
)
);
Expand Down
4 changes: 1 addition & 3 deletions tests/Container/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1109,9 +1109,7 @@ class WildcardConcrete implements WildcardOnlyInterface
{
}

/*
* The order of these attributes matters because we want to ensure we only fallback to '*' when there's no more specific environment.
*/
// The order of these attributes matters because we want to ensure we only fallback to '*' when there's no more specific environment.
#[Bind(FallbackConcrete::class)]
#[Bind(ProdConcrete::class, environments: 'prod')]
interface WildcardAndProdInterface
Expand Down
6 changes: 4 additions & 2 deletions tests/Database/DatabaseAbstractSchemaGrammarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ protected function tearDown(): void
public function testCreateDatabase()
{
$connection = m::mock(Connection::class);
$grammar = new class($connection) extends Grammar {
$grammar = new class($connection) extends Grammar
{
};

$this->assertSame('create database "foo"', $grammar->compileCreateDatabase('foo'));
Expand All @@ -26,7 +27,8 @@ public function testCreateDatabase()
public function testDropDatabaseIfExists()
{
$connection = m::mock(Connection::class);
$grammar = new class($connection) extends Grammar {
$grammar = new class($connection) extends Grammar
{
};

$this->assertSame('drop database if exists "foo"', $grammar->compileDropDatabaseIfExists('foo'));
Expand Down
31 changes: 31 additions & 0 deletions tests/Database/DatabaseConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,37 @@ protected function getMockConnection($methods = [], $pdo = null)

return $connection;
}

public function testQueryLogSizeLimit()
{
$connection = $this->getMockConnection();
$connection->enableQueryLog();
$connection->setMaxQueryLogSize(3);

$this->assertEquals(3, $connection->getMaxQueryLogSize());

for ($i = 0; $i < 5; $i++) {
$connection->logQuery('SELECT * FROM test WHERE id = ?', [$i], 10.0);
}

$queryLog = $connection->getQueryLog();

$this->assertCount(3, $queryLog);
$this->assertEquals([2], $queryLog[0]['bindings']);
$this->assertEquals([3], $queryLog[1]['bindings']);
$this->assertEquals([4], $queryLog[2]['bindings']);
}

public function testQueryLogSizeLimitMinimumValue()
{
$connection = $this->getMockConnection();

$connection->setMaxQueryLogSize(0);
$this->assertEquals(1, $connection->getMaxQueryLogSize());

$connection->setMaxQueryLogSize(-5);
$this->assertEquals(1, $connection->getMaxQueryLogSize());
}
}

class DatabaseConnectionTestMockPDO extends PDO
Expand Down
3 changes: 2 additions & 1 deletion tests/Database/DatabaseEloquentInverseRelationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,8 @@ public function testOnlyHydratesInverseRelationOnModels()
[],
new HasInverseRelationRelatedStub(),
'foo',
new class() {
new class()
{
},
new HasInverseRelationRelatedStub(),
]);
Expand Down
6 changes: 4 additions & 2 deletions tests/Encryption/EncrypterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,12 @@ public static function provideTamperedData()

return [
[['iv' => ['value_in_array'], 'value' => '', 'mac' => '']],
[['iv' => new class() {
[['iv' => new class()
{
}, 'value' => '', 'mac' => '']],
[['iv' => $validIv, 'value' => ['value_in_array'], 'mac' => '']],
[['iv' => $validIv, 'value' => new class() {
[['iv' => $validIv, 'value' => new class()
{
}, 'mac' => '']],
[['iv' => $validIv, 'value' => '', 'mac' => ['value_in_array']]],
[['iv' => $validIv, 'value' => '', 'mac' => null]],
Expand Down
Loading