I would like to bind a variable to a function's scope, I can do this in php use the 'use' keyword after PHP 5.3, however how do I do the equivalent in versions < PHP 5.3?
test_use_keyword();
function test_use_keyword(){
$test =2;
$res=array_map(
function($el) use ($test){
return $el * $test;
},
array(3)
);
print_r($res);
}
You can use a global variable, but you should always avoid globals variables whereever possible. As a suggestion, without knowing, what you are trying to solve with this
class Xy ( {
private $test;
public function __construct ($test) {
$this->test = $test;
}
public function call ($el) {
return $el * $this->test;
}
}
print_r(array_map(array(new Xy(2), 'call'), array(3));
Also possible are the good old lambdas
$test = 2;
$a = create_function ('$el', 'return $el * ' . $test . ';');
print_r (array_map($a, array(3)));
Normally through globals, seriously. Although hacks could be used to mimic the functionality, like partial functions in php. Extracted from article:
function partial()
{
if(!class_exists('partial'))
{
class partial{
var $values = array();
var $func;
function partial($func, $args)
{
$this->values = $args;
$this->func = $func;
}
function method()
{
$args = func_get_args();
return call_user_func_array($this->func, array_merge($args, $this->values));
}
}
}
//assume $0 is funcname, $1-$x is partial values
$args = func_get_args();
$func = $args[0];
$p = new partial($func, array_slice($args,1));
return array($p, 'method');
}
And only after that could you have something like.
function multiply_by($base, $value) {
return $base * $value;
}
// ...
$res = array_map(partial("multiply_by", $test), array(3));
Not... worth... it.
Related
I am writing an small ODM for Mongodb in PHP by PhpStorm. At the aggregate query:
$value = $this->creditRepo->execute(AggregateQueryBuilder::create()->project('name', 'ts')->take(10)->toQuery(),
new class{
public ?string $name;
});
the second parameter is an anonymous class. And inside of execute() function:
function execute(AggregateQuery $query, $class): ArrayObject
{
$result = $this->_collection->aggregate($query->getPipeline(), $query->getOptions());
$returnArray = new ArrayObject();
foreach ($result as $item){
$itemx = App::jsonMapper()->map($item, new $class); // $itemx is instance of $class
$returnArray->append($itemx);
}
return $returnArray;
}
The result in caller script:
$value = $this->creditRepo->execute(AggregateQueryBuilder::create()
->project('name', 'ts')->take(10)->toQuery(), new class{
public ?string $name;
});
$d = $value->getArrayCopy();
$d[0]-> // not type hint, I want it hint to me the <name>
Can PhpStorm know the output of the execute() that I can type hint the output of it?
[Edit]
I have try with function array_map
$value = $this->creditRepo->execute(AggregateQueryBuilder::create()
->project('name', 'ts')->take(10)->toQuery());
$temp = new class{
public ?string $name;
};
$output = array_map(static function ($item) use ($temp) {
$temp->name = $item['name'];
return $temp;
}, (array)$value);
$output[0]->name // type hinted
and I want to write a function that PhpStorm know that return like array_map how to do it?
Here's a little mock-up to describe my predicament:
<?php
$var = "Before";
function getVar(){
global $var;
return $var;
}
$array = Array(
"variable" => "Var = " . getVar()
);
$var = "After";
echo $array['variable'];
?>
That code would echo 'Before', I'm aiming for it to echo 'after'. I realize that this is how PHP is supposed to work however it's crucial for the array to execute getVar() only when it's called.
How would I go about doing this?
You can not do this since array declaration will initialize it - so you're mixing function calling at array's 'usage' and at it's definition. There's no 'usage': array is already defined to that moment.
However, an answer could be using ArrayAccess, like this:
class XArray implements ArrayAccess
{
private $storage = [];
public function __construct()
{
$this->storage = func_get_args();
}
public function offsetSet($offset, $value)
{
if(is_null($offset))
{
$this->storage[] = $value;
}
else
{
$this->storage[$offset] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->storage[$offset]);
}
public function offsetUnset($offset)
{
unset($this->storage[$offset]);
}
public function offsetGet($offset)
{
if(!isset($this->storage[$offset]))
{
return null;
}
return is_callable($this->storage[$offset])?
call_user_func($this->storage[$offset]):
$this->storage[$offset];
}
}
function getVar()
{
global $var;
return $var;
}
$var = 'Before Init';
$array = new XArray('foo', 'getVar', 'bar');
$var = 'After Init';
var_dump($array[1]);//'After Init'
-i.e. try to call data, which is inside element, when actual get happened. You may want to have different constructor (for associative arrays) - but the general idea was shown.
Editing my answer after the question was edited.
No, what you are trying to achieve isn't possible because when you call the function it returns and it's done at that point. But you could achieve something similar with object oriented coding. I'll create something for you, please wait.
<?php
class Foo {
public function __toString() {
global $var;
return "Var = {$var}";
}
}
$var = "Before";
$array = array( "variable" => new Foo() );
$var = "After";
echo $array['variable'];
?>
PS: Sorry for the late answer, but there was a blackout in Salzburg. :(
It occurred to me that you could also use anonymous functions and invoke/execute those
Proof of concept:
$var = "Before";
function getVar(){
global $var;
return $var;
}
$array = Array(
"variable" => create_function(null, "return 'Var = ' . getVar();")
);
$var = "After";
echo $array['variable']();
returns
Var = After
This code works but $vars can't be defined in the call() function.
Why do $vars can't be passed to the array_walk_recursive()?
class lib{
private $library;
function __construct($lib="")
{
$this->library = $lib;
}
function set($vars)
{
$decoded_classes = json_decode($this->library,true);
array_walk_recursive($decoded_classes,function(&$f) {$f = create_function($vars,$f);});
return $decoded_classes;
}
}
$json = '
{
"class1": {
"function1":"return \"$a<b>$b</b>!\";"
},
"class2": {
"function2":"return $b;",
"function3":"return $c;"
},
"function1":"return \"test\";"
}';
$lib = new lib($json);
$lib = $lib->set("$a,$b");
$lib = $lib["class1"]["function1"]("asdasasd","asdasasd");
echo $lib;
Firstly, have a look at this example with variable scoping for closures. You need to pass the variable in with the use keyword, eg:
array_walk_recursive($decoded_classes,function(&$f) use ($vars) {$f = create_function($vars,$f);});
It would be good if you defined $a, $b, etc for us, so we could actually test your code.
I'm trying to reference a private variable of an object from within a closure. The code below would seem to work, but it complains Fatal error: Cannot access self:: when no class scope is active in test.php on line 12 and Fatal error: Using $this when not in object context in test.php on line 20.
Any ideas how to accomplish the same results using a closure while keeping the variables private and without making helper functions (defeating the whole idea of a private variable).
class MyClass
{
static private $_var1;
private $_var2;
static function setVar1( $value )
{
$closure = function () use ( $value ) {
self::$_var1 = $value;
};
$closure();
}
function setVar2( $value )
{
$closure = function () use ( $value ) {
$this->_var2 = $value;
};
$closure();
}
}
MyClass::setVar1( "hello" ); //doesn't work
$myclass = new MyClass;
$myclass->setVar2( "hello" ); //doesn't work
Edit to note, this answer was originally meant for PHP5.3 and earlier, it's possible now. For current information, see this answer.
This is not directly possible. In particularly, closures have no associated scope, so they cannot access private and protected members.
You can, however, use references:
<?php
class MyClass
{
static private $_var1;
private $_var2;
static function setVar1( $value )
{
$field =& self::$_var1;
$closure = function () use ( $value, &$field ) {
$field = $value;
};
$closure();
}
function setVar2( $value )
{
$field =& $this->_var2;
$closure = function () use ( $value, &$field ) {
$field = $value;
};
$closure();
}
}
MyClass::setVar1( "hello" );
$myclass = new MyClass;
$myclass->setVar2( "hello" );
This is possible starting in PHP 5.4.0
class test {
function testMe() {
$test = new test;
$func = function() use ($test) {
$test->findMe(); // Can see protected method
$test::findMeStatically(); // Can see static protected method
};
$func();
return $func;
}
protected function findMe() {
echo " [find Me] \n";
}
protected static function findMeStatically() {
echo " [find Me Statically] \n";
}
}
$test = new test;
$func = $test->testMe();
$func(); // Can call from another context as long as
// the closure was created in the proper context.
Closures have no concept of $this or self -- they are not tied to objects in that way. This means that you would have to pass the variables through the use clause... something like:
$_var1 =& self::$_var1;
$closure = function() use ($value, &$_var1) {
$_var1 = $value;
};
$_var2 =& $this->_var2;
$closure = function() use ($value, &$_var2) {
$_var2 = $value;
};
I haven't tested the above code, but I believe it to be correct.
This question already exists:
Closed 11 years ago.
Possible Duplicate:
Any way to access array directly after method call?
In C# and other languages, I can do something like this
$value = $obj->getArray()[0];
But not in PHP. Any workarounds or am I doomed to do this all the time?
$array = $obj->getArray();
$value = $array[0];
No, you can't do it without using array_shift (Which only gets the first element). If you want to access the third or fourth, most likely you'd want to do a function like this:
function elemnt($array, $element)
{
return $array[$element];
}
$value = element($obj->getArray(), 4);
Also, see this question, as it is an exact duplicate: Any way to access array directly after method call?
I think you are doomed to do it that way :(
You can do this:
$res = array_pop(array_slice(somefunc(1), $i, 1));
If this is a one-off or occasional thing where the situation in your example holds true, and you're retrieving the first element of the return array, you can use:
$value = array_shift($obj->getArray());
If this is a pervasive need and you often need to retrieve elements other than the first (or last, for which you can use array_pop()), then I'd arrange to have a utility function available like so:
function elementOf($array, $index = 0) {
return $array[$index];
}
$value = elementOf($obj->getArray());
$otherValue = elementOf($obj->getArray(), 2);
Well, maybe this could help you, in php spl, pretty usefull, you can make you a Special Array for you:
<?php
class OArray extends ArrayObject{
public function __set($name, $val) {
$this[$name] = $val;
}
public function __get($name) {
return $this[$name];
}
}
class O{
function __construct(){
$this->array = new OArray();
$this->array[] = 1;
$this->array[] = 2;
$this->array[] = 3;
$this->array[] = 4;
}
function getArray(){
return $this->array;
}
}
$o = new O();
var_dump( $o->getArray()->{1} );
<?php
class MyArray implements Iterator ,ArrayAccess
{
private $var = array();
//-- ArrayAccess
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->var[] = $value;
} else {
$this->var[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->var[$offset]);
}
public function offsetUnset($offset) {
unset($this->var[$offset]);
}
public function offsetGet($offset) {
return isset($this->var[$offset]) ? $this->var[$offset] : null;
}//-- Iterator
public function __construct($array){
if (is_array($array)) {
$this->var = $array;
}
}
public function rewind() {
reset($this->var);
}
public function current() {
return current($this->var);
}
public function key() {
return key($this->var);
}
public function next() {
return next($this->var);
}
public function valid() {
return ($this->current() !== false);
}
}
$values = array(
"one" => 1,
"two" => 2,
"three" => 3,
);
$it = new MyArray($values);
foreach ($it as $a => $b) {
print "$a: $b<br>";
}
echo $it["one"]."<br>";?>