I have some trouble,When I define a static variable in a method and call it multiple times, the code is as follows:
function test()
{
static $object;
if (is_null($object)) {
$object = new stdClass();
}
return $object;
}
var_dump(test());
echo '<hr>';
var_dump(test());
The output is as follows:
object(stdClass)[1]
object(stdClass)[1]
Yes, they return the same object.
However, when I define a closure structure, it returns not the same object.
function test($global)
{
return function ($param) use ($global) {
//echo $param;
//exit;
static $object;
if (is_null($object)) {
$object = new stdClass();
}
return $object;
};
}
$global = '';
$closure = test($global);
$firstCall = $closure(1);
$closure = test($global);
$secondCall = $closure(2);
var_dump($firstCall);
echo '<hr>';
var_dump($secondCall);
The output is as follows:
object(stdClass)[2]
object(stdClass)[4]
which is why, I did not understand.
By calling test(...) twice in your sample code, you have generated two distinct (but similar) closures. They are not the same closure.
This becomes more obvious some some subtle improvements to your variable names
$closureA = test($global);
$firstCall = $closureA(1);
$closureB = test($global);
$secondCall = $closureB(2);
var_dump($firstCall, $secondCall);
Now consider this code alternative:
$closureA = test($global);
$firstCall = $closureA(1);
$secondCall = $closureA(2);
var_dump($firstCall, $secondCall);
Does that help you understand?
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?
I was looking at some php code and stumbled on a pipeline script. In the method to add something to the pipeline:
public function pipe(callable $stage)
{
$pipeline = clone $this;
$pipeline->stages[] = $stage;
return $pipeline;
}
The object is getting cloned, and returned.
Could someone explain me the advantages of this approach,
wouldn't the following code return the same results?
public function pipe(callable $stage)
{
$this->stages[] = $stage;
return $this;
}
No, it won't return the same. Clone creates a copy of the object, which is sometimes the desired behaviour.
class WithoutClone {
public $var = 5;
public function pipe(callable $stage)
{
$this->stages[] = $stage;
return $this;
}
}
$obj = new WithoutClone();
$pipe = $obj->pipe(...);
$obj->var = 10;
echo $pipe->var; // Will echo 10, since $pipe and $obj are the same object
// (just another variable-name, but referencing to the same instance);
// ----
class Withlone {
public $var = 5;
public function pipe(callable $stage)
{
$pipeline = clone $this;
$pipeline->stages[] = $stage;
return $pipeline;
}
}
$obj = new WithClone();
$pipe = $obj->pipe(...);
$obj->var = 10;
echo $pipe->var; // Will echo 5, since pipe is a clone (different object);
i want to turn a simple string like that "response->dict->words" into a variable name that i can actually work with. I will give an example now. Lets assume the value of $response->dict->words is 67.
Example:
$var = "response->dict->words"
echo $$var; /* PRINT THE VALUE 67 FROM $response->dict->words*/
As you may notice i put an extra dollar sign before the $var because this should work, but it doesn't.
Can anyone help me with this?
class ClassOne {
public function test() {
return 'test';
}
}
class ClassTwo {
public function test2() {
return 'test2';
}
}
$one = new ClassOne();
$two = new ClassTwo();
$objects = array('one', 'two');
$methods = array('test', 'test2');
for ($i = 0; $i < count($objects); $i++) {
echo ${$objects[$i]}->$methods[$i]();
}
You can store classnames or method names as strings and later use them, or even store variable names, like here ${$objects} (variable variables), but you cannot store whole logic.
To evaluate whole logic, you have to use eval(), which is most probably bad idea
$var = "response->dict->words"
eval("?> <?php echo $".$var.";");
You can split your string and make the call as below:
class Response {
public $dict;
public function __construct() {
$this->dict = new stdClass();
$this->dict->words = 'words test';
}
}
$response = new Response();
$var = 'response->dict->words';
$elements = explode('->', $var);
echo ${$elements[0]}->$elements[1]->$elements[2];
Results into words test
Or, if you don't know the level of nesting the object call, you can perform the call in a foreach loop. When the loop exits, the last call will be available after it:
class Response {
public $dict;
public function __construct() {
$this->dict = new stdClass();
$this->dict->words = new stdClass();
$this->dict->words->final = 'test chained string';
}
}
$response = new Response();
$var = 'response->dict->words->final';
$elements = explode('->', $var);
foreach ($elements as $key => $element) {
if ($key == 0) {
$call = ${$element};
continue;
}
$call = $call->$element;
}
echo $call;
Results into: test chained string
There is a better way, why don't you cache the variable like
$var = $response->dict->words;
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
Can we dynamically create and initialize an object in PHP?
This is the normal code:
class MyClass{
var $var1 = null;
var $var2 = null;
.
.
public function __construct($args){
foreach($args as $key => $value)
$this->$key = $value;
}
}
---------------------
$args = ($_SERVER['REQUEST_METHOD'] == "POST") ? $_POST : $_REQUEST;
$obj = new MyClass($args);
The above code works fine. Please note that the names of REQUEST parameters are accurately mapped with the members of class MyClass.
But can we do something like this:
$class = "MyClass";
$obj = new $class;
If we can do like this, then can we initialize $obj by using $args.
According to this post, $obj = $class should work. But it does not work for me. I tried get_class_vars($obj). It threw an exception.
Thanks
It's more a comment, but I leave it here more prominently:
$class = "MyClass";
$obj = new $class($args);
This does work. See newDocs.
You have to overload some other magic methods:
__get (a method that gets called when you call object member)
__set (a method that gets called when you want to set object member)
__isset
__unset
Please see this codepad to see your code rewritten to work with what you want:
<?php
class MyClass{
var $properties = array();
public function __construct($args){
$this->properties = $args;
}
public function __get($name) {
echo "Getting '$name'\n";
if (array_key_exists($name, $this->properties)) {
return $this->properties[$name];
}
return null;
}
}
$args = array("key1" => "value1", "key2" => "value2");
$class = "MyClass";
$obj = new $class($args);
echo "key1:". $obj->key1;
?>
You can use Reflection to instanciate an object with parameters.
<?php
class Foo {
protected $_foo;
protected $_bar;
public function __construct($foo, $bar)
{
$this->_foo = $foo;
$this->_bar = $bar;
}
public function run()
{
echo $this->_foo . ' ' . $this->_bar . PHP_EOL;
}
}
$objectClass = 'Foo';
$args = array('Hello', 'World');
$objectReflection = new ReflectionClass($objectClass);
$object = $objectReflection->newInstanceArgs($args);
$object->run();
See Reflection on php manual.