Storing closures in thread fails - php

I'm trying to use a closure as a callback once a thread is done running. However I'm running into what seems to be a limit/failure of PHP or the pthread extension.
My dev stack is running on Win7 x64 with PHP 5.5.3 x86 TS, pthread version 0.44.
The following code works :
class Test
{
public $callbackVar;
}
$test = new Test();
$callbackVar = function()
{
echo "Callback var invoked.";
};
$test->callbackVar = $callbackVar;
$test->callbackVar->__invoke();
But as soon as I derive Test from Thread, running the script gives an error :
class Test extends Thread
{
public $callbackVar;
public function run() { }
}
$test = new Test();
$callbackVar = function()
{
echo "Callback var invoked.";
};
$test->callbackVar = $callbackVar;
// assert() returns true
assert($test->callbackVar === null);
$test->callbackVar->__invoke();
With the following output
Fatal error: Call to a member function __invoke() on a non-object
Anyone ever had this issue ? Any possible workaround ? I'd rather not use eval if possible... I've tried many workarounds, such as rewrapping into another closure, using a ReflectionFunction, ... nothing cuts it.

Zend does not allow you to serialize closure objects.
So it's not something you should try to work around, possibly at some time in the future Zend will allow serialization of Closures, pthreads will not require changes at that time.
You'll just have to do it the old fashioned way ...

Related

PHP 8.1 DOMDocument serialization

I'm trying to make the following class compatible with native PHP serialization, specifically when running on PHP 8.1.
class SerializableDomDocument extends DOMDocument
{
private $xmlData;
public function __sleep(): array
{
$this->xmlData = $this->saveXML();
return ['xmlData'];
}
public function __wakeup(): void
{
$this->loadXML($this->xmlData);
}
}
It's all fine and dandy on lower PHP versions, but 8.1 yields Uncaught Exception: Serialization of 'SerializableDomDocument' is not allowed whenever such an object is attempted to be passed to serialize() function. Here's a sample of the code that would produce such an exception: https://3v4l.org/m8sgc.
I'm aware of the __serialize() / __unserialize() methods introduced in PHP 7.4, but using them doesn't seem to be helping either. The following piece of code results into the same exception as can be observed here: https://3v4l.org/ZU0P3.
class SerializableDomDocument extends DOMDocument
{
public function __serialize(): array
{
return ['xmlData' => $this->saveXML()];
}
public function __unserialize(array $data): void
{
$this->loadXML($data['xmlData']);
}
}
I'm quite baffled by this problem, and would really appreciate any hints. For the time being it seems like the only way forward would be to introduce an explicit normalizer/denormalizer, which would result in a breaking change in the codebase API. I'd like to avoid that.
On 10 Aug 2021, this change was commited to version 8.1 RC1:
Mark DOM classes as not serializable
So you can no longer serialize those classes.
It seems this is related to invalid methods or invalid XML content in your DOMDocument. If you do not use it, this works just fine https://3v4l.org/K91Vv

php 7.3 - Class constants as default param values crashing

I have some code that has been working for a long time, but is now producing perplexing errors. I have to assume this was caused by my moving to php 7.3, but I can't find any references that explain what is happening.
I have a method in a class that looks something like:
class Foo {
function bar($param = OtherClass::MY_CONSTANT) {
logger(__METHOD__);
logger(OtherClass::MY_CONSTANT);
logger($param);
logger('ready to do stuff');
// does stuff
}
}
Where OtherClass::MY_CONSTANT = 1.
When I call that method from another class:
$foo = new Foo();
$foo->bar();
the output is something like:
DEBUG - Foo::bar
DEBUG - 1
and then php execution stops abruptly, not even calling my registered shutdown function. I am able to use the class constant directly in the body of the method, but I am not able to use the parameter set to the class constant's value by default.
If I change the method to
function bar($param = 1) {
everything works fine.
Also, if I pass the constant when I call the method it works:
$foo = new Foo();
$foo->bar(OtherClass::MY_CONSTANT);
I get the happy
DEBUG - Foo::bar
DEBUG - 1
DEBUG - 1
DEBUG - ready to do stuff
and execution continues normally.
Something about using a class constant as a default is making the variable poisonous, even though I can use the class constant in the method without any trouble.
I tried to create a simple one-file example to recreate this problem, but it worked just fine. Aargh.
Has something changed in php 7.3 that would cause this behavior? I just upgraded to 7.3.1 but the problem persisted. Is there a better practice I should be using?
EDIT TO ADD:
After a few hours trying to make a simple case to reproduce this, I have to move on. Relevant factors include:
running in php-fpm
code running after closing the connection to the browser.
even constants in the same class (self::MY_CONSTANT) create poison variables
My solution was to roll back to php 7.2.14, which is working properly. I can only assume this in a bug in php 7.3.
As a workaround, you can do it like this:
function bar($param = null) {
if ($param === null) {
$param = OtherClass::MY_CONSTANT;
}
logger(__METHOD__);
logger(OtherClass::MY_CONSTANT);
logger($param);
logger('ready to do stuff');
// does stuff
}
This assumes that null is not a valid value for the parameter; replace that with some other invalid value if it is. If the parameter can legitimately be anything, this workaround won't work.

CakePHP 3.0 "thread class not found"

I want to implement the threading concept in CakePHP 3.0 ,
But when I try to extend the thread class, it gives an error of "Thread class not found"
I have also implemented it in core php and its working as expected,
But somehow its not working with cakephp.
Here is the corephp code
<?php
class AsyncOperation extends Thread {
public function __construct($arg) {
$this->arg = $arg;
}
public function run() {
if ($this->arg) {
$sleep = rand(1,60);
for ($i=0; $i < 100 ; $i++) {
sleep(1);
echo $this->arg."----------->".$i."<br/>";
}
}
}
}
class CallingClass {
public function runScript($var)
{
print_r("start run script");
$th = new AsyncOperation($var);
$th->start();
print_r("continue running");
}
}
$wow = new AsyncOperation("First");
$wow->start();
$wow2 = new AsyncOperation("Last");
$wow2->start();
?>
And in CakePHP 3
class AsyncOperation extends Thread
You want to learn about namespaces in php. Cake and almost every lib these days uses them. You need to use the use keyword and import the class from another namespace if it does not exist within the namespace your current class is in. Or, not really best practice, provide the absolute namespace.
Also I'm not sure what you try to do, but instead of threads I would recommend to take a look at work queues likes RabbitMQ or ZeroMQ.
Your php version doesn't have the thread class. By default, if you install it on LINUX, you won't have the thread class.
You need to download the php source code, enable the zts and then compile it.
This is how I did on linux:
Enable zts on redhat - pthreads on php
Just add simple line
use Thread;

Was something()->Something always valid php? Is this fine to use?

Examples I have seen for my thing first put this into a variable. It pulls in a lot of data. A object I guess. But i just need one little piece of it.
$data = something();
echo $data->Something;
I just tried this never seen this in any php code and just tryed. Is this something always worked even in ond php versions? Is nobody doing it for a reason, or did I just not saw enough code??
echo something()->Something;
It works fine.
Referencing an object property from the return of a function has been supported since PHP introduced classes (PHP version 5.0.0).
This can be demonstrated with the following simple test case:
class Something {
public $foo;
}
function get() {
$obj = new Something;
$obj->foo = 'bar';
return $obj;
}
echo get()->foo;
This will print bar on every PHP version >= 5.0.0.

In PHP, how to quickly access objects property from a return?

My problem is this.
A function return an object, and I want to access its property. It throws and error when I try to do it like this
$this->FunctionThatReturnsAnObject()->Property;
Right now I'm creating a new variable and taking the property from it, like this:
$newVar=$this->FunctionThatReturnsAnObject();
$property=$newVar->Property;
Is this the right way of doing this?
This works perfectly fine in modern PHP versions.
[mcg#mcg-workstation ~]$ php -a
Interactive shell
php > class Foo { public function functionThatReturnsAnObject() { return new Bar(); } }
php > class Bar { public $property = 'Hello, world.'; }
php > $f = new Foo();
php > echo $f->functionThatReturnsAnObject()->property;
Hello, world.
php >
If you're experiencing an error, we'll need to know what the error is. As mentioned in the comments, you might not be able to do this in PHP4, but you shouldn't be using PHP4 in the modern era to begin with. As long as the instance method you're calling returns an object, you can operate on that object directly. (You can't do this from a constructor right now: new Foo()->functionThatReturnsAnObject() will not work because the PHP internals team is full of people that think this would be confusing in some way. I'm not making this up.)

Categories