Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unpacking array behavior is weird when using makePartial method. #1220

Open
wadakatu opened this issue Mar 3, 2023 · 3 comments
Open

Unpacking array behavior is weird when using makePartial method. #1220

wadakatu opened this issue Mar 3, 2023 · 3 comments

Comments

@wadakatu
Copy link

wadakatu commented Mar 3, 2023

Q A
PHPUnit version 9.6.4
Mockery version 1.5.0
PHP version 8.1.12
Installation Method Composer

Summary

The behavior of array spread operator (...) is not expected when I use makePartial method.

Current behavior

Without makePartial method.
    // test method
    public function testMethodCallOnce()
    {
        $course = new Course();

        (new ActualController($course))->index();
    }

---
"a"
array:3 [
  "email" => "test@test.com"
  "role" => "test_role"
  "last_name" => "aaa"
]
With makePartial method
    // test method
    public function testDumpMethodCallOnce()
    {
        $course = Mockery::mock(Course::class)

        (new ActualController($course))->index();
    }

---
"a"
[]

index method and dump method are like below.

    // ActualController Class
    public function index()
    {
        $this->course->dump('abc', ...[
            'email' => 'test@test.com',
            'role' => 'test_role',
            'last_name' => 'aaa',
        ]);
    }
    // Course Class
    public function dump(string $abc, ...$attributes)
    {
        // I use Laravel.
        dd($abc, $attributes);
    }

How to reproduce

Expected behavior

I expect that both output are same.

Without makePartial method.
    // test method
    public function testMethodCallOnce()
    {
        $course = new Course();

        (new ActualController($course))->index();
    }

---
"abc"
array:3 [
  "email" => "test@test.com"
  "role" => "test_role"
  "last_name" => "aaa"
]
With makePartial method
    // test method
    public function testDumpMethodCallOnce()
    {
        $course = Mockery::mock(Course::class)

        (new ActualController($course))->index();
    }

---
"abc"
array:3 [
  "email" => "test@test.com"
  "role" => "test_role"
  "last_name" => "aaa"
]

I have no idea what causes this behavior.
Perhaps, I misunderstood something about phpunit.
If you need more info and code, please let me know.
Thank you.

@davedevelopment
Copy link
Collaborator

Weird one, I don't think you've misunderstood anything. Simple test case:

<?php

use Mockery\Adapter\Phpunit\MockeryTestCase;

require "vendor/autoload.php";

class Foo {
    public function bar($a, ...$b) {
        return [$a, $b];
    }
}

class test extends MockeryTestCase
{
    /** @test */
    public function a_test()
    {
        $mock = Mockery::mock(Foo::class)->makePartial();

        $res = $mock->bar('abc', ...['def' => 'hjk']);

        self::assertEquals('abc', $res[0]);
        self::assertEquals(['def' => 'hjk'], $res[1]);
    }
}

@wadakatu
Copy link
Author

@davedevelopment

Thank you for providing simple test case.
It helps me a lot to debug.

I did some research about this behavior.
It seems like makePartial method has nothing to do with this behavior.

Apparently, func_get_args method which is a built-in function in PHP is the reason.
https://www.php.net/manual/ja/function.func-get-args.php

Mockery uses this method as follows.

public function bar($a, ...$b){
    $argc = func_num_args(); // get the number of function's arguments.
    $argv = func_get_args(); // get the content of function's arguments.
    $ret = $this->_mockery_handleMethodCall(__FUNCTION__, $argv); // execute method with arguments.
    return $ret;
}

if I define bar method something like below...

$foo = new Foo();
$foo->bar('abc', ...['def' => 'hjk']);

func_num_args and func_get_args returns something like this.

1 // func_num_args()
array:1 [ // func_get_args()
  0 => "abc"
]

It shows that my second argument ...['def' => 'hjk'] is gone.

I do not know whether or not this behavior is intentional.
Would you tell me about it, if you know anything?

@davedevelopment
Copy link
Collaborator

As of PHP 8.0.0, the func_*() family of functions is intended to be mostly transparent with regard to named arguments, by treating the arguments as if they were all passed positionally, and missing arguments are replaced with their defaults. This function ignores the collection of unknown named variadic arguments. Unknown named arguments which are collected can only be accessed through the variadic parameter.

I assume this is the problem. Really not sure how we get around it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants