Repository: laravel/framework (php)

Issue:
Collection methods don't work with built-in PHP functions
<!-- DO NOT THROW THIS AWAY -->
<!-- Fill out the FULL versions with patch versions -->

- Laravel Version: #.#.#
- PHP Version: #.#.#
- Database Driver & Version:

### Description:

Built-in PHP functions can't be used with `Collection` methods.

### Steps To Reproduce:

In PHP there are a lot of types of `callable`, for example `Closure` is callable, anonymous function, also `[$this, 'method']` and `"strtoupper"` for example.

This kind of code work fine:
```php
$collection = collect(['one']);

$collection->map(function ($value) { return strtoupper($value); });
```
but this one doesn't
```php
$collection = collect(['one']);

$collection->map('strtoupper');
```

it throws
```
ArgumentCountError : strtoupper() expects exactly 1 argument, 2 given
```

#### About PHP functions

`"strtoupper"` is a valid callable in PHP, for example

```php
is_callable("strtoupper"); // true
```
and also
```php
$function = "strtoupper";
$function("one"); // "ONE"
```

It also works fine with `array_map()`:
```php
array_map(['one', 'two'], 'strtoupper'); // ["ONE", "TWO"]
```

Pull request description:
[9.x] Arr::map - Fix map-by-reference w/ built-ins
This PR fixes fallback support for `map` by-reference with built-in's such as `strrev` that do not accept more than one argument, making it slightly more flexible, predictable and forgiving in behavior, as in the majority of situations the `$key` argument is omitted/ignored by the developer anyway.

While some situations _might_ remain unsolved for some built-ins (ones that have exactly 2 parameters), I think it's important to mention that in such situations the user would be better off with a different or lower-level method such as `array_map`  instead, [as Laravel's documentation clearly indicates that a `$key` arg is passed within the `map` method](https://laravel.com/docs/9.x/collections#method-map).

---

```
ArgumentCountError: strrev() expects exactly 1 argument, 2 given in [..]/Illuminate/Collections/Arr.php:558
Stack trace:
#0 [internal function]: strrev('usb', 0)
```

Closes #42789


Patch:
diff --git a/src/Illuminate/Collections/Arr.php b/src/Illuminate/Collections/Arr.php
index c217ea8e48..731fa2949d 100644
--- a/src/Illuminate/Collections/Arr.php
+++ b/src/Illuminate/Collections/Arr.php
@@ -2,6 +2,7 @@
 
 namespace Illuminate\Support;
 
+use ArgumentCountError;
 use ArrayAccess;
 use Illuminate\Support\Traits\Macroable;
 use InvalidArgumentException;
@@ -555,7 +556,11 @@ public static function map(array $array, callable $callback)
     {
         $keys = array_keys($array);
 
-        $items = array_map($callback, $array, $keys);
+        try {
+            $items = array_map($callback, $array, $keys);
+        } catch (ArgumentCountError) {
+            $items = array_map($callback, $array);
+        }
 
         return array_combine($keys, $items);
     }
diff --git a/src/Illuminate/Support/Facades/Mail.php b/src/Illuminate/Support/Facades/Mail.php
index 0097834b52..0d18c2b5db 100755
--- a/src/Illuminate/Support/Facades/Mail.php
+++ b/src/Illuminate/Support/Facades/Mail.php
@@ -29,6 +29,7 @@
  * @method static void assertNotSent(string $mailable, callable|int $callback = null)
  * @method static void assertNothingQueued()
  * @method static void assertNothingSent()
+ * @method static void assertNothingOutgoing()
  * @method static void assertQueued(string|\Closure $mailable, callable|int $callback = null)
  * @method static void assertSent(string|\Closure $mailable, callable|int $callback = null)
  *


Test patch:
diff --git a/src/Illuminate/Support/Testing/Fakes/EventFake.php b/src/Illuminate/Support/Testing/Fakes/EventFake.php
index ea752c74ff..97b11bb49c 100644
--- a/src/Illuminate/Support/Testing/Fakes/EventFake.php
+++ b/src/Illuminate/Support/Testing/Fakes/EventFake.php
@@ -53,7 +53,7 @@ public function __construct(Dispatcher $dispatcher, $eventsToFake = [])
      * Assert if an event has a listener attached to it.
      *
      * @param  string  $expectedEvent
-     * @param  string  $expectedListener
+     * @param  string|array  $expectedListener
      * @return void
      */
     public function assertListening($expectedEvent, $expectedListener)
diff --git a/tests/Support/SupportArrTest.php b/tests/Support/SupportArrTest.php
index d09fe6cd9a..a4f5bf0ce6 100644
--- a/tests/Support/SupportArrTest.php
+++ b/tests/Support/SupportArrTest.php
@@ -632,6 +632,15 @@ public function testMap()
         $this->assertEquals(['first' => 'taylor', 'last' => 'otwell'], $data);
     }
 
+    public function testMapByReference()
+    {
+        $data = ['first' => 'taylor', 'last' => 'otwell'];
+        $mapped = Arr::map($data, 'strrev');
+
+        $this->assertEquals(['first' => 'rolyat', 'last' => 'llewto'], $mapped);
+        $this->assertEquals(['first' => 'taylor', 'last' => 'otwell'], $data);
+    }
+
     public function testPrepend()
     {
         $array = Arr::prepend(['one', 'two', 'three', 'four'], 'zero');