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

Serialization of 'Closure' is not allowed #451

Closed
willmot opened this issue Jan 2, 2012 · 13 comments
Closed

Serialization of 'Closure' is not allowed #451

willmot opened this issue Jan 2, 2012 · 13 comments
Labels
type/bug Something is broken

Comments

@willmot
Copy link

willmot commented Jan 2, 2012

Although $GLOBAL Closures are no longer serialised since 01aa347 & #352 a $GLOBAL array which contain a Closure as a value will still cause the "Serialization of 'Closure' is not allowed" error.

$GLOBALS[] = array( 'foo' => function() { return 'bar'; }  );
@bakert
Copy link

bakert commented Aug 9, 2012

@tmilos
Copy link

tmilos commented Feb 8, 2013

Another case I'm getting it

/* test.php */
class closureTest extends PHPUnit_Framework_TestCase
{
    function testClosure() {
        $this->assertEquals(1, 1);
        call_user_func(function($a) {
            throw new \Exception("test");
        }, 10);
        $this->assertEquals(2, 2);
    }
}

and then run it in process isolation

phpunit --process-isolation test.php

you would get

There was 1 error:
1) phTest::testClosure
PHPUnit_Framework_Exception: PHP Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed' in -:37
Stack trace:
#0 -(37): serialize(Array)
#1 -(123): __phpunit_run_isolated_test()
#2 {main}
thrown in - on line 37
FAILURES!
Tests: 1, Assertions: 0, Errors: 1.

@muteor
Copy link

muteor commented May 7, 2013

I was having the same issue when using process isolation, to work around it I added __sleep to my testCase and cleaned up the closures in there. For me I had stored some mocks in properties of the testcase so it was my own fault really, I hope this helps someone.

@thomas-p-wilson
Copy link

I am having the same issue as well, and can replicate the issue @tmilos mentioned earlier. I don't know if this ticket is dead, but it would be nice if we could resolve this. I'm more than willing to put in a pull request if that's preferable.

@whatthejeff
Copy link
Contributor

Pull requests are always appreciated!

@thomas-p-wilson
Copy link

I know it's been a bit, but I was off-project for a while. Further to the comment by @tmilos, I can replicate the problem if I force a syntax error, for example:

/* test.php */
class closureTest extends PHPUnit_Framework_TestCase
{
    function testClosure() {
        call_user_func(function($a) {
            throw new \Exception("test");
        });
    }
}

Note the lack of a provided argument at the end of call_user_func. The serialization exception results from the attempt to serialize the PHPUnit_Framework_TestResult object. This serialization attempt can be found on line 42 of PHPUnit/Framework/Process/TestCaseMethod.tpl.dist. The offending part of the to-be-serialized object is a closure reference in the trace hierarchy of the Exception that was thrown:

[1] => Array
    (
        [function] => {closure}
        [class] => Issue451Test
        [type] => ->
        [args] => Array
            (
            )

)

My question then, as this is my first time patching PHPUnit, is short of stripping the closure or modifying it to be otherwise benign - which would involve the use of Reflection no doubt - is there some PHPUnit-best-practice way to deal with this? Any thoughts or input at least?

@thomas-p-wilson
Copy link

Also, I wrote a test under Regressions/GitHub. Is this acceptable?

@thomas-p-wilson
Copy link

@kunjalpopat, in reference to your issue, The first thing I would check is for exceptions. When in process isolation, thrown exceptions may give you a "Serialization of Closure" message. Try wrapping your test code in a try/catch and see if that fixes the problem, if it does, then you may want to look at trimming down the coverage of the try/catch until you find the problem area.

@stafr
Copy link

stafr commented Jun 5, 2014

Problem lies in PHPUnit_Util_GlobalState::backupGlobals:

if ($key != 'GLOBALS' &&
    !in_array($key, $superGlobalArrays) &&
    !in_array($key, $blacklist) &&
    !$GLOBALS[$key] instanceof Closure) {  // <-- this is the problem
    self::$globals['GLOBALS'][$key] = serialize($GLOBALS[$key]);
}

I've made up some helper in PHPUnit_Util_GlobalState:

public static function checkIfThereIsClosureInIt($arr) {
    if ($arr instanceof Closure)
        return true;
    if (is_object($arr))
        $arr = get_object_vars($arr);
    if (is_array($arr))
        foreach ($arr as $x)
            if (PHPUnit_Util_GlobalState::checkIfThereIsClosureInIt($x))
                return true;
    return false;
}

and changed a little backupGlobals function:

foreach (array_keys($GLOBALS) as $key) {
    if ($key != 'GLOBALS' &&
        !in_array($key, $superGlobalArrays) &&
        !in_array($key, $blacklist) &&
        !PHPUnit_Util_GlobalState::checkIfThereIsClosureInIt($GLOBALS[$key])
//        !$GLOBALS[$key] instanceof Closure
    ) {
        self::$globals['GLOBALS'][$key] = serialize($GLOBALS[$key]);
    }
}

this version seems to be working (I don't catch that exception any more).
Since it's a recursion against $GLOBALS, so, it can give some extra heat to cpu.

@paultorben
Copy link

The above code fix from stafr fixed the PHPunit issues I was having with the "Serialization of Closure" exceptions. Many thanks!

@cbrunnkvist
Copy link

I have this same error message whenever I instantiate a new MyRestClient(), the constructor of which contains $this->client = new \GuzzleHttp\Client();. It is also the same when I tried using a $x=function(){}; in the constructor of my class. I guess Guzzle client does something similar internally. This makes it impossible to ever instantiate from within a phpunit test. I'm so sad now 😭

@rawaludin
Copy link

Just adding my experience. Basically whenever you put anonymous function or class as test class attribute, phpunit will throw Serialization of 'Closure' is not allowed or Serialization of 'class@anonymous' is not allowed. So, if you need to use anonymous function or class, make sure its one time variable, not test class attribute.

@mpyw
Copy link

mpyw commented Jun 23, 2021

I wrote the patch: mpyw/phpunit-patch-serializable-comparison

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/bug Something is broken
Projects
None yet
Development

No branches or pull requests