static variable in php closure defined within a private class method - php

So I re-wrote this to hopefully be clearer about what I'm attempting. During some parsing of data someValue(s) associated with someCode(s) are received. The intent is to capture a particularValue of a someCode having the lowest precedence, irrespective of the order the someCode/someValue (s) received. The issue keeping this from working is that the closure is created every time the private function is called and the value of $precedenceOfCodeCaptured therefore is always reset to null. If I could keep the closure around then things might work as intended.
private function Foo($particularValue, $someValue, $someCode) {
switch ($someCode) {
case:
CODE1:
case:
CODE2:
$c = function () use ($someCode, $someValue) {
static $precedenceOfCodeCaptured = null;
$precedenceArray = array(
CODE2 => 1,
CODE1 => 2
);
if ((is_null($someValue))) {//first time the case statement matched because $someValue==null
$precedenceOfCodeCaptured = $precedenceArray[$someCode];
$particularValue = someValue;
} else if ($precedenceArray[$someCode] <= $precedenceOfCodeCaptured) {
$particularValue = someValue;
}
};
$c();
break;
...
}
}//end of private method

Every time Foo is called, you create a new function/closure, since you evaluate a function(), and that's what evaluating function() does!
If you'd like to have Foo() return the same function/closure each time, only evaluate it once. Perhaps use a singleton pattern, where you'll check if some higher-scoped variable that holds the function has been initialized, and initialize it if not.
For example:
$c = null;
private function Foo(){
if ($c == null) {
$c = function() use ( $whatever){
static $x = 0;
echo "x= $x";
$x++;
...
};
}
$c();
}//end of private method
(but please don't use globals in your actual code!)

Try this:
static $x = 0;
private function Foo() {
$c = function() use ( $whatever){
echo "x= $x";
self::$x++;
...
};
$c();
}

<?php
class example{
private function foo(){
static $_closure;
if( ! $_closure ){
$_closure = function(){
static $_state;
/* . . . */
};
}
$_closure();
}
}
Note that the closure is static (it is created on first use, and not again), as is the variable that holds state inside the closure.
However, as stated in other answers, this is "pointless" at best. $_closure should be another class method; $_state should be a class property (there is no need for static anything in the closure, in foo(), nor in the class). It is not "cleaner" or more readable or performant (in fact, the opposite is likely true) to use a closure like this.
It might make sense if you were doing something like:
returning the closure for use elsewhere
dynamically creating the function at runtime (i.e., based on foo() args)
something else dynamic and interesting
…but as it stands, this is simply the wrong approach. The tools to do this already exist.

Related

Calling member function on array

I don't truly understand how chaining functions work on the values that are returned.
Let's say I have a function that returns a string or array
public static $query;
public static function getArray($arr) {
Database::$query = $arr;
return Database::$query;
}
public function single() {
return Database::$query[0];
}
Why, when I call it can I then not chain a function onto this to affect the string (In this example I was to append ' test' and how would I go about doing this?
Why can I simply not call
Database::getArray(array("test","test2"))->single();
Without getting a Call to a member function single() on array error. But instead, make it return only the first value of the array.
How would I go append doing what I'm trying to achieve here? Why is my logic wrong?
When you call a method, the return value is whatever that method decides to return; the return value doesn't have any automatic relationship with the object you called the method on. For instance:
class A {
public function foo() {
return 'Hello, World!';
}
}
$a = new A;
echo $a->foo();
The value returned is just an ordinary string, just as if foo was a global function not attached to any object.
In PHP, strings (and other "basic types" like arrays) are not objects, so you can't call any methods on them. Even if you could, those methods would be built into the language, and you couldn't just decide that ->single() could be called on any array.
What may be confusing is that some people write methods with the convention that they return an object, known as a "fluent interface", or more generally "chained methods". This is not a feature of the language, just a natural consequence of returning an object from a method:
class A {
public function foo() {
return new B;
}
}
class B {
public function bar() {
return 'Hello, World!';
}
}
$a = new A;
$b = $a->foo(); // $b is a B object
echo $b->bar();
// We can combine this into one line:
echo (new A)->foo()->bar();
There is nothing special about this chaining; it's just that wherever you have an object, you can call appropriate methods on it, just as wherever you have a number, you can do maths with it. Compare with a simple addition:
function foo() {
return 1;
}
$a = foo();
$a = $a + 2;
echo $a;
// We can combine this into one line:
echo foo() + 2;
// Or keep the assignment:
$a = foo() + 2;
echo $a;
The object doesn't know it's being chained - in fact, it shouldn't need to know anything about the code around it, and that's an important part of structured programming.
A common pattern is then to have modifying methods which return the object they just modified, so you can make a series of modifications in one go:
class A {
private $words = [];
public function addWord($word) {
$this->words[] = $word;
// $this is the current object, which is an instance of class A
return $this;
}
public function getString() {
return implode(' ', $this->words);
}
}
$a = new A;
// Calling $a->addWord(...) gives us back the same object
$a = $a->addWord('Hello');
$a = $a->addWord('World');
// Calling $a->getString() gives us back a string
echo $a->getString();
// We can combine this into one line:
echo (new A)->addWord('Hello')->addWord('World')->getString();
Note that you can only refer to $this if you have created an instance of the object (with the new keyword), not in a method declared as static. A static method can still have this kind of pattern, but it will need to return some other object, like new self (a new instance of the current class) or self::$foo (an object created earlier).
it's called fluent interface, if you want to chain methods from same class you have to return this from each of them which you want to call fluently, so your code should look like:
public static $query;
public function getArray($arr) {
Database::$query = $arr;
return $this;
}
public function single() {
return Database::$query[0];
}
after applying changes, the construct Database::getArray(array("test","test2"))->single(); will work, however you may consider renaming method getArray, because as its name suggests, it shouldn't be returning $this, but array
#EDIT
you should change the type of function getArray from public static function to public function to make it work, also your final statement will change to something like:
(new Database())->getArray(array("test","test2"))->single();
however, in this case, I would consider redesigning your class and creating some kind of singleton so that you instantiate Database class only once and store the object somewhere

How to reassigned a function from a reference in PHP?

Say I have a function from an Object :
class Cat {
protected $sound = 'MeOwWw~';
public function make_a_big_and_nice_sound () { echo $this->sound; }
}
$C = new Cat;
$C->make_a_big_and_nice_sound ();
Now, the function's name could be long and the content depends on some properties of the object itself so It can't be rewrite outside of the Object.
But let's say I kind of have only one cat to birth and I want to make it meow a lot of time in my code here and there. What I want to do is the following :
main.php
function please_meow = $C->make_a_big_and_nice_sound;
please_meow ();
If your method names are long there is a big chance your method is simple doing too much. A method should only do one single thing.
So you should really actually fix your code instead.
Considering you don't provide your actual code (which would have helped pointing out the actual flaw in your code) and if you really insist on doing this (you really shouldn't) you could use a closure for this:
$please_meow = function() use ($C) {
return $C->make_a_big_and_nice_sound();
};
$please_meow();
But again if you need this you are doing it wrong.
If you use PHP 5.3 or above, try this:
$please_meow = function() use($C) { $C->make_a_big_and_nice_sound(); };
$please_meow();
Now, the function's name could be long and the content depends on some properties of the object itself so It can't be rewrite outside of the Object.
But let's say I kind of have only one cat to birth and I want to make
it meow a lot of time in my code here and there
You can do something like this
class Cat
{
protected static $instance;
function __construct()
{
self::$instance = $this;
}
public static function please_meow()
{
self::$instance->make_a_big_and_nice_sound();
}
protected $sound = 'MeOwWw~';
public function make_a_big_and_nice_sound () { echo $this->sound; }
}
new Cat();
Cat::please_meow();
Cat::please_meow();
// some other codes
Cat::please_meow();

Determining if a closure is static in PHP

A closure defined in PHP can also carry the static modifier.
$f = function () { };
$g = static function () { };
The static closure cannot be bound via Closure::bind or Closure::bindTo, and will issue a warning.
$g = Closure::bind(static function () { }, new stdClass());
// Warning: Cannot bind an instance to a static closure in ...
This is also the case of closures created by reflecting a static method with ReflectionMethod::getClosure.
class MyClass
{
public static function myStaticMethod() { }
}
// reflect MyClass::myStaticMethod, create an unbound closure, and try to bind it
$f = (new ReflectionMethod(MyClass::class, 'myStaticMethod'))
->getClosure()
->bindTo(new stdClass());
// Warning: Cannot bind an instance to a static closure in ...
While annoying, this is acceptable; however, how is one to test between a static and non-static closure?
ReflectionMethod::isStatic seemed like it might work, but sensibly doesn't as Closure::__invoke is instance-level, not static.
$f = static function () { };
// reflect Closure::__invoke because I think I'm tricky
$r = new ReflectionMethod($f, '__invoke');
// and it's not static anyway
var_dump($r->isStatic()); // bool(false)
Further, checking ReflectionMethod::getClosureThis can generally work, as a static method must be unbound, however that doesn't cover closures defined outside an instance method, or the corner-case of instance methods that have been unbound.
class MyClass
{
public function myInstanceMethod() { }
}
$o = new MyClass();
// reflect MyClass::myInstanceMethod, create a bound closure, and then unbind it
$f = (new ReflectionMethod($o, 'myInstanceMethod'))
->getClosure($o)
->bindTo(null);
// then reflect the closure
$r = new ReflectionFunction($f);
// and see it's bound to nothing, as would be the case of a static closure
var_dump($r->getClosureThis()); // NULL
So, to restate, how do you determine whether a closure is static (or more specifically, bindable) or not?
It really seems as though we should have a ReflectionFunctionAbstract::isBindable, or that ReflectionMethod::isStatic be moved up to ReflectionFunctionAbstract.
If binding works, the Closure will have a $this bound to it. So, just bind it and then check for the $this. If it's null, well, then it's a static Closure.
function isBindable(\Closure $closure) {
$boundClosure = #\Closure::bind($closure, new stdClass);
return $boundClosure && (new ReflectionFunction($boundClosure))->getClosureThis() != null;
}
It seems impossible now.
You can find some debates here: https://bugs.php.net/bug.php?id=64761
The only real workaround I use for myself now is adding ->isBindable property manually.
Here's some code I found here https://github.com/atoum/atoum/blob/master/classes/test/adapter/invoker.php
Maybe will give you a few ideas
protected static function isBindable(\closure $closure)
{
$isBindable = (version_compare(PHP_VERSION, '5.4.0') >= 0);
if ($isBindable === true)
{
$reflectedClosure = new \reflectionFunction($closure);
$isBindable = ($reflectedClosure->getClosureThis() !== null || $reflectedClosure->getClosureScopeClass() === null);
}
return $isBindable;
}
Very old question but still shows up in google search. The mentioned isStatic nowadays exists in ReflectionFunction,as of php 8.1 (I tested it out)
$fn = static fn() => true;
$refl = new ReflectionFunction($fn);
$refl->isStatic(); // returns true
$fn = fn() => true;
$refl = new ReflectionFunction($fn);
$refl->isStatic(); // returns false
It does not show up in documentation as of writing.
See: https://www.php.net/manual/en/class.reflectionfunction.php
I also created a missing doc report here: https://github.com/php/doc-en/issues/2136

Get the number of time a class method has been invoked?

Let's say I have this class.
class foo{
function a(){
return $this;
}
}
And I instantiate it as:
$O = new foo();
$O->a()
->a()
->a();
Is there any way to know, in that last function ->a() how many times it was called before?
So, I could output like 'method ->a() has been called twice before this.'
I would like to find out this, without using increment values like declaring a property and then increment it increment it, everytime it is called in a function.
I am just hopping if there is a hidden feature in OOP that can provide for this solution
You can use a static variable inside the method:
class foo{
function a(){
// $count will be initialized the first time a() was called
static $count = 0;
// counter will be incremented each time the method gets called
$count ++;
echo __METHOD__ . " was called $count times\n";
return $this;
}
}
Note that static has a different meaning when used inside a method or function and it has nothing to do with static class members - although it is the same keyword. It means that the variable will be created and initialized only once when the method has been called for the first time.
However, in the example above there is no way to reinitialize that counter. If you want to do that you may introduce a parameter or something like this. Also you may not use a static variable but an object property.. There are tousand ways to do it, tell me your exact application needs I may give a more specific example....
In comments it was suggested to use a decorator for this job. I like this idea and will give a simple example:
class FooDecorator
{
protected $foo;
protected $numberOfCalls;
public function __construct($foo) {
$this->foo = $foo;
$this->reset();
}
public function a() {
$this->numberOfCalls++;
$this->foo->a();
return $this;
}
public function resetCounter() {
$this->numberOfCalls = 0;
}
public function getNumberOfCalls() {
return $this->numberOfCalls;
}
}
Usage example:
$foo = new FooDecorator(new foo());
$foo->a()
->a()
->a();
echo "a() was called " . $foo->getNumberOfCalls() . " times\n";

Confused on how php objects handle arrays

The output of this is "24", when I'd expect "44".
class some_class {
public $array = array();
public function construct() {
$this->array[5] = 4;
}
public function something() {
// $this->array at this point is empty, why?
echo (isset($this->array[5])) ? $this->array[5] : 2;
$this->array[5] = 4;
// Here, $this->array holds the value 4 in the key 5 correctly
echo (isset($this->array[5])) ? $this->array[5] : 2;
}
}
$some_object = new some_class();
$some_object->something();
Any ideas why my expectations are being shattered?
Your constructor isn't firing it needs to be called:
public function __construct(){
// constructor
}
otherwise the array fails to initialize.
Your question basically boils down to your line at the beginning of something(), asking:
$this->array at this point is empty, why?
This is the case because PHP constructors need to be named __construct, whereas yours is simply named construct.
Your function construct() is never called. You should name it __construct().

Categories