Is passing variables to php anonymus function without use legit? - php

I just learned about php anonymous functions and found out that the following code is correct:
Example1:
//$mysqli = new mysqli(...)
(function($x, $y, $conn) {
echo $x, $y; //$x and $y are not visible outside
})(786, 333, $mysqli);
Over the internet I have found examples like
Example2:
$x = 786; $y = 333;
(function($x, $y) {
echo $x, $y;
})();
Or
Example3:
$x = 786; $y = 333;
(function() use($x, $y){
echo $x, $y;
})();
I couldn't find any php documentation or code on stackoverflow which suggest example1 is correct syntax. Please tell me with official reference if example1 like syntax is allowed in php.

The first example is effectively defining the function and then calling it with the values. Something like...
(definition of function)(values to pass);
You can split into two parts to show how this works...
$fn =function($x, $y, $conn) {
echo $x, $y; //$x and $y are not visible outside
};
($fn)(786, 333, $mysqli);

//$mysqli = new mysqli(...)
(function($x, $y, $conn) {
echo $x, $y; //$x and $y are not visible outside
})(786, 333, $mysqli);
This is perfectly valid, and just follows from two things:
The function(...) { ... } syntax creates and returns a Closure object
A Closure object can be invoked directly as though it was a function name
There is no special syntax, it's just one of an infinite number of ways you can put the building blocks of PHP together.
There are lots of other combinations you can do for the same reason, e.g.:
class Foo
public static function make_function() {
return function($x, $y, $conn) {
echo $x, $y;
};
}
}
(Foo::make_function())(786, 333, $mysqli);
But also:
class Foo
public static function make_function($conn) {
return function($x, $y) use ($conn) {
echo $x, $y;
};
}
}
(Foo::make_function($conn))(786, 333, $mysqli);
As with your example, specifying a use clause doesn't make much difference in this specific case because we're executing the function straight away. But it allows you to "capture" a value in one place and then pass the other arguments later, e.g.
class Foo
public static function make_function($conn) {
return function($x, $y) use ($conn) {
echo $x, $y;
};
}
}
$f = Foo::make_function($mysqli);
// later...
$f(786, 333);

Related

A parameter of constructor is "constants", is there a design-pattern / elegant solution?

This is mostly an imaginary problem, no real life situation, but it's simply do depict with.
In this example we cache a point to a directory. I used to have
common.php
define('PATH_TO_CACHE', '/var/www/cache');
pixel.php
class Pixel
{
private int $x, $y;
public function __construct(int $x, int $y)
{
$this->x = $x;
$this->y = $y;
}
public function draw()
{
// drawing...
file_put_contents(PATH_TO_CACHE.'/test.php', var_export([$this->x, $this->y], true));
}
}
$a = new Pixel(1,4);
$b = new Pixel(4,1);
$c = new Pixel(1,1);
this is so far very simple, but that way it's not so flexible, our code is tight to PATH_TO_CACHE.
After refactoring:
class Pixel
{
private readonly int $x, $y;
private readonly string $pathToCache;
public function __construct(int $x, int $y, string $pathToCache)
{
$this->x = $x;
$this->y = $y;
$this->pathToCache = $pathToCache;
}
public function draw()
{
// drawing...
file_put_contents($this->pathToCache.'/test.php', var_export([$this->x, $this->y], true));
}
}
this is then fully independent now, but how do we instantiate Pixel?
$a = new Pixel(1,4, '/var/www/cache');
$b = new Pixel(4,1, '/var/www/cache');
$c = new Pixel(1,1, '/var/www/cache');
now let me not summarize why it is bad, starting out from redundancy nightmare.
But I cant figure a real solution. How to do it? And let's try keep this object immutable. And Pixel itself cant have that constants, since what if this Pixel can be reused among totally different projects?

Why when passing a method as argument to another method do I get 'Function name must be a string'

In the following trivial example, I am simply assigning the arguments of array $x one after the other to method Cmeth. The crashing statement should be doing something like Aobject->Cmeth("ABC") but something is hosed somewhere!
<?php
class A
{
public function Cmeth($z)
{
// do something
}
}
class B
{
public function Dmeth(array $w, array $x)
{
foreach ($x as $y) {
$w[0]->$w[1]($y); // crashes with "Function name must be a string"
}
}
}
$Bobj = new B;
$u = ["ABC", "DEF"];
$w = [new A, "Cmeth"];
$Bobj->Dmeth($w, $u);
You need to enclose $w[1] in {} to correctly parse the expression:
$w[0]->{$w[1]}($y);
Otherwise it gets interpreted as
($w[0]->$w)[1]($y)
which is why it thinks your function name is an array, not a string.
Demo on 3v4l.org

Vector calculations class oop php

I'm learning php oop and seems like i still do not understand of how some things work as my code, which, imo, looks properly doesn't work and returns few errors. This is the code:
<?php
class Vector {
private $x;
private $y;
public function __construct($x, $y) {
if (is_nan($x) || is_nan($y)) {
$this->x = 0 && $this->y = 0;
} else {
$this->set($x, $y);
}
}
public function __destruct() {
var_dump($x, $y);
}
public function setx($x) {
if (is_numeric($x)) {
$this->x;
}
}
public function sety($y) {
if (is_numeric($y)) {
$this->y;
}
}
public function retLength() {
return $x;
}
public function addVector() {
$sum=$x+$y;
}
public function dotProduct() {
$dot_product = array_sum(array_map(function($x,$y) { return $x*$y; }, $array1, $array2));
}
}
$wekt= new Vector($x, $y);
echo $wekt->addVector(5,7);
Errors i get are: undefined variables "x" and "y" on line 42 (which is $wekt= new Vector($x, $y); ) and "Call to undefined method Vector::set()".
Specification for this class is:
two private attributes $x and $y (seems to be ok)
constructor receives $x and $y and on receive checks if these are numbers. Constructor is supposed to output message about just created vector.
I do not understand much all this constructor and probably this is one of reasons why this code doesn't work as it is intended to.
destructor is supposed to output info about destroyed object.
there should be available functions to change values of $x and $y
there should be available function to return $x
two more functions: one outputting sum of two vectors, second function supposed to output scalar product which takes as value a number.
There are two things in your code going wrong. See the following code:
class Vector {}
$wekt= new Vector($x, $y);
echo $wekt->addVector(5,7);
Where are $x and $y defined? This is the main reason you are getting the error. You class has a different scope then the global scope, meaning everything you define there is only accessible by $wekt-> as $wekt is defined in the global scope.
Now the second thing is addVector():
echo $wekt->addVector(5,7);
You ask to echo the return of this method, yet no return is defined in that function. Secondly where is the $x and $y defined within the scope of that function?
Change it to something like:
class Vector {
private $list = [];
public function addVector($x, $y) {
$this->list[] = new Vector($x, $y);
return $x + $y;
}
}

Mock object and retain functionality of other methods

Say for example I have this class:
class Foo {
public function add($x, $y)
{
return $x + $y;
}
public function subtract($x, $y)
{
return $x - $y;
}
}
and I wanted to change the behavior of the add method only:
$mock = $this->getMock('Foo');
$mock->expects($this->once())->method('add')->will($this->returnCallback(function ($x, $y) {
return ($x + 0) + ($y + 0);
}));
$this->assertEquals(4, $mock->add(2,2));
$this->assertEquals(2, $mock->subtract(4,2));
Why is my subtract method now returning null? I was expecting it to behave usual.
Failed asserting that null matches expected 2.
You need to do a partial mock specifying to getMock what method you want to mock:
$mock = $this->getMock('Foo', array('add');
In this way only the add method is mocked, the rest of the object is behaving as usual.
Use like this:
$mock = m::mock('Foo[add]');

how to access variable of a method

I have this class and I want to get the values in second method runSecond from Gettest method. How would I do that?
class Test {
public static function Gettest($x, $y, $z){
$x = $x;
$x = $x . basename($y);
self::runSecond();
}
private function runSecond(){
//how do I access $x here? I need to know the value of $x, $y and $z here
// and I dont want to pass it like this self::runSecond($x, $y, $z)
}
}
Why do you not want to pass the values into your second method?
Method parameters are the accepted way of doing this.
The only other option you have is to use global or member variables, but for something like this I would highly suggest parameters instead. There is no good reason I can see not to.
If you really, absolutely, have to do this (and I still don't see why), you can use a private member variable like this:
class Test {
private $x;
private $y;
private $z;
public static function Gettest($x, $y, $z){
$x = $x;
$x = $x . basename($y);
$test = new Test();
$test->x = $x;
$test->y = $y;
$test->z = $z;
$test->runSecond();
}
private function runSecond(){
$this->x;
$this->y;
$this->z;
}
}
Note that you have to create an instance of the class to call the second method. Your original way of using self:: would not work to call a non static method, even if you did pass the values as parameters.

Categories