opis/closure

Opis Closure 4.x is available for testing

msarca opened this issue ยท 22 comments

Hi, everyone!

I'm happy to announce that we are about to launch a new version of Opis Closure.

This version targets PHP +7.4 and use FFI to make closures truly serializable, without wrapping them inside a SerializableClosure object. This means that you no longer need to adapt your library's logic in order to have serializable closures. One line of code and closures will become serializable.

Please give it a try and let us know if you have encountered any issues while testing.

Version 4.0 does not work:
Serialization Closure is broken. $reflection->getCode() returns <?php and namespace ... sometimes

You can try to fix your bugs with running test in yiisoft/composer-config-plugin#82
Run tests: ./vendor/bin/phpunit --debug
See generated files in tests/Integration/Environment/vendor/yiisoft/composer-config-plugin-output/ (params.php or web.php).

@xepozz If you believe you have discovered a bug, please open a new issue so we could treat it properly. Also, you must be aware that Opis Closure 4.x is incompatible with 3.x and generates the code differently than you might expect. Our goal is to make closures serializable. If you are using this library for different purposes, than it might not be the most appropriate tool.

Just to be clear for everyone reading this thread: our public API consist of a single function Opis\Closure\init. Everything else is internal. Please use this library strictly for closure serialization/unserialization. If you want to analyse a closure's source code nikic/php-parser will help you do a better job.

@xepozz If you believe you have discovered a bug, please open a new issue so we could treat it properly. Also, you must be aware that Opis Closure 4.x is incompatible with 3.x and generates the code differently than you might expect. Our goal is to make closures serializable. If you are using this library for different purposes, than it might not be the most appropriate tool.

We are using this package for serializing/unserializing standalone closures and closures in a properties.

@xepozz Please open a new issue and provide the most basic example that can be reproduced. Thanks.

@xepozz I did take a look at your test results and the first thing that struck me was this: Error: Class 'Environment\Serializer\CustomSerializer' not found. Incidentally that is the class where closure serialization was supposed to be happen.

@xepozz I did take a look at your test results and the first thing that struck me was this: Error: Class 'Environment\Serializer\CustomSerializer' not found. Incidentally that is the class where closure serialization was supposed to be happen.

I've fixed it

We added PHP 8 support

A huge feature of Laravel is queued Closures with proper ORM model serialization / deserialization when the models are part of the use portion of the Closure. We accomplished this in previous versions of Opis by overwriting methods that I had PR'd to Opis...

My original PR to Opis: #21

Our overwritten methods can be seen here: https://github.com/illuminate/queue/blob/master/SerializableClosure.php

Can this still be accomplished in 4.x? Again, this is a very large and widely used feature of Laravel so if it's not planned to be supported for 4.x we will likely be forced to fork 3.x and maintain that version ourselves unfortunately. ๐Ÿ˜ฌ

Can this still be accomplished in 4.x?

Not at this time, but a similar mechanism can be added in 5 minutes.

However, in 4.x there are no wrappers, meaning that transformUseVariables/resolveUseVariables handlers will apply globally.

Also, since we removed the wrappers you can view a closure as other data type, like an array (from serialization/unserialization perspective), so this should work:

$a = function () use ($laravalModel, $laravelCollection) {};
$b = [$laravelModel, $laravelCollection]; //  array serialization is handled by PHP, so you don't have transform/resolve handlers

unserialize(serialize($a)); // works because we have transform/resolve handlers
unserialize(serialize($b)); // should also work. 

If it doesn't work properly then the problem is not related to opis/closure (in my opinion, @msarca what do you think?).

Again, this is a very large and widely used feature of Laravel so if it's not planned to be supported for 4.x we will likely be forced to fork 3.x and maintain that version ourselves unfortunately.

We just added @GrahamCampbell to opis/closure team to help maintain version 3.x for laravel as long as necessary (see #63).

It would not be a significant problem for us for those handlers to apply globally I don't think.

Hey, PHP-DI maintainer here ๐Ÿ‘‹ We're using opis/closure too, and I'm worried about the usage of FFI: AFAIK that extension isn't installed by default, is that correct?

@taylorotwell is the FFI requirement an issue with Laravel?

@mnapoli We are also dependant on ext-tokenizer #92, and nobody ever complained ๐Ÿ˜…. The FFI extension is a bundled extension, just like ext-tokenizer, ext-pdo, or ext-mbstring, and I don't think it will be an issue.

The PHP dpcumentation is still warning that the FFI extension is experimental. Using the extension requires the "libffi" library to be installed, and PHP has to be compiled using "--with-ffi".

I have my doubts that distributions will do that by default, and would consider relying on that extension to be risky.

I checked two installations I use: Ubuntu seems to have FFI enabled by default (I did not remember installing it explicitly), but Suse Linux Enterprise does not - as this is used in my production system, I'd have to investigate what is offered as optional extension there.

I got feedback from my ops team: Suse Linux is not yet providing the FFI extension. If we'd ask them to provide it, it would go only into their latest release, which is SLES15SP3.

Hi,

I guess version 4.x doesn't work with PHP 8.1, at least for me.

Dockerfile:

FROM php:8.1-fpm
RUN docker-php-source extract \
&& apt-get update \
&& apt-get install libffi-dev && docker-php-ext-configure ffi --with-ffi && docker-php-ext-install ffi \
&& docker-php-source delete

php.ini:

extension=ffi.so
ffi.enable = true

test.php

<?php

declare(strict_types=1);

require __DIR__ . '/../vendor/autoload.php';

\Opis\Closure\Library::init();

$f = fn() => 'Hello';
$data = serialize($f);
$g = unserialize($data);
echo $g(); //> Hello

Output:

tomaskudelka@tomas-air sandbox % php test.php

Fatal error: Uncaught Exception: Serialization of 'Closure' is not allowed in /var/www/html/www/index.php:11
Stack trace:
#0 /var/www/html/www/index.php(11): serialize(Object(Closure))
#1 {main}
  thrown in /var/www/html/www/index.php on line 11

With PHP 8.0 and same configuration everything works fine.

@driesvints removed your comment for being off topic

@msarca sure, it's your privilege to maintain this repo how you prefer. Was just trying to help @thorewi

@driesvints yes, thank you, I know the another library...

@thorewi will take a look and try to fix it. @driesvints I appreciate your understanding.

A huge feature of Laravel is queued Closures with proper ORM model serialization / deserialization when the models are part of the use portion of the Closure. We accomplished this in previous versions of Opis by overwriting methods that I had PR'd to Opis...

My original PR to Opis: #21

Our overwritten methods can be seen here: https://github.com/illuminate/queue/blob/master/SerializableClosure.php

Can this still be accomplished in 4.x? Again, this is a very large and widely used feature of Laravel so if it's not planned to be supported for 4.x we will likely be forced to fork 3.x and maintain that version ourselves unfortunately. ๐Ÿ˜ฌ

I have written a new serialization library that supports serializing closures without wrapping them. Also it supports serializing readonly Closure properties. I haven't released it yet (it will be part of another library instead) but if there is need I can.