[php-src] Issue #8202: Getting the scoped variables and their values from a closure using reflection
| From: | Wuffz | Date: | Tue, 15 Mar 2022 19:03:11 +0000 |
| Subject: | [php-src] Issue #8202: Getting the scoped variables and their values from a closure using reflection | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-240347@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/8202
Author: Wuffz
### Description
I was trying something to parse closures as statements.
Here is a small example using existing functions of what i am actually trying to do
```php
<?php
$max= 12;
$refclass = new \ReflectionClass( fn($u) => $u->age > $max);
$scope= $refclass->getProperties(); // or maybe $refclass->getUsedVariables(); something like
that?
var_dump($scope);
// can we access maxage with the value 12 ?
```
Below is a bigger test as a proof of concept,
Note that it is just a proof of concept and it does showcase a use for it
```php
<?php
class Query {
public function __construct(
private $context
){}
/** irrelevant functions **/
private function getChunkOfCode($start, $stop) {
$lines = file(__FILE__);
$buff = [];
for ( $i = $start-1; $i < $stop-1; $i ++ ) {
$buff[] = trim($lines[$i],"\r\n");
}
return implode("\n", $buff);
}
/** end of irrelevant functions */
public function where(Closure $l) {
// below is a simple example.
// in a real world scenario i would probably use a
// simple AST to parse the actual expressions and validate them or fallback to actual
filterable callbacks.
// get as reflection function.
$ref = new \ReflectionFunction($l);
// get the function body of the closure
$output = $this->getChunkOfCode(
$ref->getStartLine(),
$ref->getEndLine()
);
// this should be smarter. but for demo purposes clean it up a bit.
$output = explode ( "=>", $output );
$output = trim($output[1]);
// use reflection class to parse into any syntax, in this demo its an sql 'where'
statement
$refcontext = new \ReflectionClass($this->context);
// parse to some sql format for demo purposes
foreach ( $ref->getParameters() as $param ) {
foreach ( $refcontext->getProperties() as $prop ) {
$output = str_replace(
'$'.$param->getName().'->'.$prop->getName(),
'
'.$this->context::$EntityName.'.'.$prop->getName().'',
$output
);
}
}
// this is where i would try to parse the scoped vars into the output.
var_dump($output);
}
}
class UserEntity{
public static $EntityName = 'user_entity';
public function __construct(
public $name
){
}
}
$q = new Query(UserEntity::class);
// this will work as the scope is not used.
$q->where(
fn($c) => $c->name == 12
);
// this will not work, as the scope cannot be accessed
$q->where(
fn($c) => $c->name == $otherage
);
```
I am willing to help out here, if needed let me know how.