hoaproject / File

The Hoa\File library.
https://hoa-project.net/
323 stars 20 forks source link

fclose(): 16442 is not a valid stream resource #25

Closed j0k3r closed 8 years ago

j0k3r commented 8 years ago

In wallabag we are using RulerZ to implement custom rule for auto-tagging.

Everything works great until few days ago (we really don't know why) when a failing test occurs in a particular environment (PHP 5.6 & PostgreSQL). It doesn't happend in any other version of PHP and any other SGBD. And we aren't able to reproduce it in our local env with the same versions :disappointed:

Here is the failing build: https://travis-ci.org/wallabag/wallabag/jobs/174123711 (and here are other builds in context: https://travis-ci.org/wallabag/wallabag/builds/174008567).

For an unknown reason, closing a stream on __destruct is making the build to fail.

The test is checking that a particular operator isn't available (length is this case, see here).

Here is the plain stack trace :

[1] InvalidArgumentException: fclose(): 16442 is not a valid stream resource
    at n/a
        in /home/travis/build/wallabag/wallabag/vendor/hoa/file/File.php line 244

    at fclose(\'Resource id #16442\')
        in /home/travis/build/wallabag/wallabag/vendor/hoa/file/File.php line 244

    at Hoa\\File\\File->_close()
        in /home/travis/build/wallabag/wallabag/vendor/hoa/stream/Stream.php line 316

    at Hoa\\Stream\\Stream->close()
        in /home/travis/build/wallabag/wallabag/vendor/hoa/stream/Stream.php line 628

    at Hoa\\Stream\\Stream->__destruct()
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/Form/Util/OrderedHashMap.php line 164

    at Symfony\\Component\\Form\\Util\\OrderedHashMap->getIterator()
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/Form/Util/OrderedHashMap.php line 164

    at Symfony\\Component\\Form\\Util\\OrderedHashMap->getIterator()
        in  line 

    at IteratorIterator->__construct(object(OrderedHashMap))
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/Form/Form.php line 383

    at Symfony\\Component\\Form\\Form->setData(object(Config))
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/Form/Form.php line 477

    at Symfony\\Component\\Form\\Form->initialize()
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/Form/FormBuilder.php line 226

    at Symfony\\Component\\Form\\FormBuilder->getForm()
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/Form/FormFactory.php line 39

    at Symfony\\Component\\Form\\FormFactory->create(\'Wallabag\\CoreBundle\\Form\\Type\\RssType\', object(Config), array(\'action\' => \'/config#set2\'))
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php line 309

    at Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller->createForm(\'Wallabag\\CoreBundle\\Form\\Type\\RssType\', object(Config), array(\'action\' => \'/config#set2\'))
        in /home/travis/build/wallabag/wallabag/src/Wallabag/CoreBundle/Controller/ConfigController.php line 92

    at Wallabag\\CoreBundle\\Controller\\ConfigController->indexAction(object(Request))
        in  line 

    at call_user_func_array(array(object(ConfigController), \'indexAction\'), array(object(Request)))
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php line 153

    at Symfony\\Component\\HttpKernel\\HttpKernel->handleRaw(object(Request), \'1\')
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php line 68

    at Symfony\\Component\\HttpKernel\\HttpKernel->handle(object(Request), \'1\', true)
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php line 169

    at Symfony\\Component\\HttpKernel\\Kernel->handle(object(Request))
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Client.php line 79

    at Symfony\\Component\\HttpKernel\\Client->doRequest(object(Request))
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Client.php line 131

    at Symfony\\Bundle\\FrameworkBundle\\Client->doRequest(object(Request))
        in /home/travis/build/wallabag/wallabag/vendor/symfony/symfony/src/Symfony/Component/BrowserKit/Client.php line 315

    at Symfony\\Component\\BrowserKit\\Client->request(\'GET\', \'/config\')
        in /home/travis/build/wallabag/wallabag/tests/Wallabag/CoreBundle/Controller/ConfigControllerTest.php line 500

    at Tests\\Wallabag\\CoreBundle\\Controller\\ConfigControllerTest->testTaggingRuleCreationFail(array(\'tagging_rule[rule]\' => \'length(domainName) <= 42\', \'tagging_rule[tags]\' => \'cool tag\'), array(\'The operator\', \'does not exist.\'))
        in  line 

    at ReflectionMethod->invokeArgs(object(ConfigControllerTest), array(array(\'tagging_rule[rule]\' => \'length(domainName) <= 42\', \'tagging_rule[tags]\' => \'cool tag\'), array(\'The operator\', \'does not exist.\')))
        in /home/travis/build/wallabag/wallabag/vendor/phpunit/phpunit/src/Framework/TestCase.php line 1103

    at PHPUnit_Framework_TestCase->runTest()
        in /home/travis/build/wallabag/wallabag/vendor/phpunit/phpunit/src/Framework/TestCase.php line 954

    at PHPUnit_Framework_TestCase->runBare()
        in /home/travis/build/wallabag/wallabag/vendor/phpunit/phpunit/src/Framework/TestResult.php line 701

    at PHPUnit_Framework_TestResult->run(object(ConfigControllerTest))
        in /home/travis/build/wallabag/wallabag/vendor/phpunit/phpunit/src/Framework/TestCase.php line 909

    at PHPUnit_Framework_TestCase->run(object(PHPUnit_Framework_TestResult))
        in /home/travis/build/wallabag/wallabag/vendor/phpunit/phpunit/src/Framework/TestSuite.php line 753

    at PHPUnit_Framework_TestSuite->run(object(PHPUnit_Framework_TestResult))
        in /home/travis/build/wallabag/wallabag/vendor/phpunit/phpunit/src/Framework/TestSuite.php line 753

    at PHPUnit_Framework_TestSuite->run(object(PHPUnit_Framework_TestResult))
        in /home/travis/build/wallabag/wallabag/vendor/phpunit/phpunit/src/Framework/TestSuite.php line 753

    at PHPUnit_Framework_TestSuite->run(object(PHPUnit_Framework_TestResult))
        in /home/travis/build/wallabag/wallabag/vendor/phpunit/phpunit/src/TextUI/TestRunner.php line 465

    at PHPUnit_TextUI_TestRunner->doRun(object(PHPUnit_Framework_TestSuite), array(\'listGroups\' => false, \'loader\' => null, \'useDefaultConfiguration\' => true, \'verbose\' => true, \'testSuffixes\' => array(\'Test.php\', \'.phpt\'), \'configuration\' => \'/home/travis/build/wallabag/wallabag/phpunit.xml.dist\'))
        in phar:///home/travis/.phpenv/versions/5.6.5/bin/phpunit/phpunit/TextUI/Command.php line 152

    at PHPUnit_TextUI_Command->run(array(\'/home/travis/.phpenv/versions/5.6/bin/phpunit\', \'-v\'), true)
        in phar:///home/travis/.phpenv/versions/5.6.5/bin/phpunit/phpunit/TextUI/Command.php line 104

    at PHPUnit_TextUI_Command::main()
        in /home/travis/.phpenv/versions/5.6.5/bin/phpunit line 722

I've really now idea why a stream is opened when we create a form ... but it seems to fail when closing it. Did you ever encountered that issue?

I was thinking of a fix in Stream.php on line 626:

    public function __destruct()
    {
+       if (false === $this->isOpened()) {
+           return;
+       }

        $this->close();
        return;
    }

What do you think?

Hywan commented 8 years ago

Hello @j0k3r,

So, this is going to be very hard if you are not able to reproduce it :-/. What this form does? Is it the ruler editor? If yes, then do you “lint” the rule? If yes, you might open a stream at a particular time. What could it fail? A stream is a black-box/an opaque resource type. Maybe PgSQL closes the resource and when trying to close it again, it fails. This is my assumption.

Your patch makes a lot of sense. In a perfect world, it would be useless, but streams are never stable, so it makes sense.

Are you willing to do a PR yourself?

Thank you for the bug report and the constructive attitude :+1:!

j0k3r commented 8 years ago

This form allow user to add a custom rule :

image

As far as I know, the rule is parsed and validated by RulerZ, so I guess it is linted. What kind of stream does it open? Is it a physical one (like file on the file system) or a memory one?

It seems weird to me that PostgreSQL close that kind of stream. I'll submit a PR and try to configure wallabag to use my fork before validating it solve the problem

Hywan commented 8 years ago

To be frank, I have no clue. PgSQL should not have nothing to do with Hoa, and the resource type is resource so very opaque. The form is closing a stream, apparently a file, which might be linked to the Sf form.

j0k3r commented 8 years ago

Looks like it fix the problem:

j0k3r commented 8 years ago

One last question, there is no tests on Hoa\Stream? :thinking:

Hywan commented 8 years ago

@j0k3r Not yet. This is under migration. I am currently working on it!

CircleCode commented 8 years ago

maybe I come late in the battle, but I was wondering if the difference between your own env and travis would not be in the way Travis handles connection to postgresql (by using persistent connection, pgbouncer, or something else you would not use) and that would cause a different management of the connection's resource

j0k3r commented 8 years ago

Maybe, but it was also tied to PHP 5.6. This bug didn't appear using PHP > 5.6