Availability of method vars within a sub-function - php

Referencing a method parameter variable from within a sub-function of that method doesn't seem to work even when global is specified.
public function sortArray(&$array, $keyToCompare){// BOOL sortArray( ARR &$array, STR $keyToCompare )
function cmpVals($pairA, $pairB){
global $keyToCompare;
return strcmp($pairA[$keyToCompare], $pairB[$keyToCompare]);
}
return uasort($array, 'cmpVals');
}
Is it even possible to reference a method parameter from within a sub-function? If so... how?
For my specific example above I realise I could use a closure to achieve the same goal but I don't want the script to be PHP 5.3 dependent.

Any reason you can't make it static?
class YourClassName {
public static $keyToCompare;
public function sortArray(&$array, $keyToCompare){// BOOL sortArray( ARR &$array, STR $keyToCompare )
self::$keyToCompare = $keyToCompare;
function cmpVals($pairA, $pairB){
$keyToCompare = YourClassName::$keyToCompare;
return strcmp($pairA[$keyToCompare], $pairB[$keyToCompare]);
}
return uasort($array, 'cmpVals');
}
}

You seemed to be already using OOP in PHP 5.3. Might as well create a sorter class?
class Sorter{
private $key;
function __construct($key){
$this->key = $key;
}
private function compare($a, $b){
return strcmp($a[$this->key], $b[$this->key]);
}
public function sort($a){
uasort($a, array($this, 'compare'));
}
}

Another option is an anonymous function:
public function sortArray(&$array, $keyToCompare){
return uasort($array, function($pairA, $pairB) uses ($keyToCompare) {
return strcmp($pairA[$keyToCompare], $pairB[$keyToCompare]);
});
}
Untested, on a train :D, but see the documentation for more info.
FYI, the pass by reference is unnecessary since you don't modify the array.. PHP won't make a copy so there is no memory issues passing by value.

Related

Can an anonymous function return itself?

In some PHP quiz I got the following task - I have to return true on the following:
function foo($x)
{
return $x === $x();
}
foo(__________ALLOWED_INPUT____________);
Now my idea was to pass an anonymous function which returns itself:
foo(function() { return $this_function; })
However I did not yet figure out a way to do this. Is it possible somehow?
PS: Nice Game (https://returntrue.win/?level=6).
You can create an anonymous function that returns a reference to itself:
foo($x=function()use(&$x){return$x;})
http://sandbox.onlinephpfunctions.com/code/743f72c298e81e70f13dc0892894911adfb1b072
[Whitespace is for readability only; all of these should work on one line if required.]
As a variation on Alexandre Elshobokshy's answer, you could use a (super-)global variable instead of a use statement to give the function access to itself:
foo(
$GLOBALS['x'] = function() {
return $GLOBALS['x'];
}
);
Or you could let the function find itself in the call stack using debug_backtrace:
foo(
function() {
$backtrace = debug_backtrace();
return $backtrace[1]['args'][0];
}
)
Inspired by a comment from Spudley about returning a function name, you can actually declare a function within the scope allowed by wrapping it in an IIFE:
foo(
(function(){
function f(){ return 'f'; }
return 'f';
})()
);
An anonymous function cannot return a reference to itself as far as I know, and there is no built-in PHP function that returns a reference to itself as far as I know, so that would leave an invokable class. That could work:
new class{function __invoke(){return $this;}}
answers-to-returntrue.win-with-explanations

Passing function name as callback doesn't work as expected in my class

This seems straightforward, but the below code is giving the following error. Any suggestions?
usort() expects parameter 2 to be a valid callback, function 'cmp' not
found or invalid function name
My code:
function cmp($item1, $item2) {
return strcmp(strtolower($item1->last_name), strtolower($item2->last_name));
}
public function get_people() {
usort($this->my_array, 'cmp');
}
Since you use $this->my_array and the function has the keyword public, I'm going to assume these two methods are in a class definition, so you also have to define, that you want to call a class method and not a normal function.
This means you have to change:
usort($this->my_array, 'cmp');
to:
usort($this->my_array, [$this, 'cmp']);
//^^^^^ So it will call the class method and not a normal global function
It seems you have this within a class so there's two ways you can do this.
first way, by telling it the method exists on the current class
public function get_people() {
usort($this->my_array, array($this, 'cmp'));
}
second way, using closures
public function get_people() {
usort($this->my_array, function($item1, $item2) {
return strcmp(strtolower($item1->last_name), strtolower($item2->last_name));
});
}
I personally prefer the closure way as this function is only used by this sort function.
Yes, you're inside a class. There are many ways how to use class or object functions for callback, see PHP manual. Example:
public function get_people() {
usort($this->my_array, array($this, 'cmp'));
}

Convert static method to lambda in PHP

I want to get static method from class and copy it to variable.
This is non-working example illustrating my question:
class foo
{
public static function bar($argument){ return 2*$argument; }
}
$class = new ReflectionClass('foo');
// here is no ReflectionMethod::getClosure() method in reality
$lambda = $class->getMethod('bar')->getClosure();
echo $lambda(3);
So my question: is this possible by any normal way? I find only one way for now. I can parse source file, get method source from it and convert it using create_function() but it's too perverse.
Just wrap it with closure.
$lamda = function($argument){return foo::bar($argument);};
Or you can try to use something like this
function staticMethodToClosure($class, $method) {
return function($argument)use($class, $method){return $class::$method($argument);};
}
An array in the format array($className, $methodName) is invokable as a static method call so this may work for you.
class foo
{
public static function bar($argument){ return 2*$argument; }
public static function getStaticFunction($arg){
return array("foo", $arg);
}
}
$a = foo::getStaticFunction("bar");
echo $a(5); // echos 10

PHP object method doesn't behave as I expect

I can't quite understand why the output of this code is '1'.
My guess is that php is not behaving like most other OO languages that I'm used to, in that the arrays that php uses must not be objects. Changing the array that is returned by the class does not change the array within the class. How would I get the class to return an array which I can edit (and has the same address as the one within the class)?
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function getArr()
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = $t->getArr();
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
EDIT: I expected the output to be 0
You have to return the array by reference. That way, php returns a reference to the array, in stead of a copy.
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function & getArr() //Returning by reference here
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = &$t->getArr(); //Reference binding here
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
This returns 0.
From the returning references subpage:
Unlike parameter passing, here you have to use & in both places - to
indicate that you want to return by reference, not a copy, and to
indicate that reference binding, rather than usual assignment, should
be done
Note that although this gets the job done, question is if it is a good practice. By changing class members outside of the class itself, it can become very difficult to track the application.
Because array are passed by "copy on write" by default, getArr() should return by reference:
public function &getArr()
{
return $this->arr;
}
[snip]
$tobj_arr = &$t->getArr();
For arrays that are object, use ArrayObject. Extending ArrayObject is probably better in your case.
When you unset($tobj_arr[0]); you are passing the return value of the function call, and not the actual property of the object.
When you call the function again, you get a fresh copy of the object's property which has yet to be modified since you added 5 to it.
Since the property itself is public, try changing:
unset($tobj_arr[0]);
To: unset($t->arr[0]);
And see if that gives you the result you are looking for.
You are getting "1" because you are asking PHP how many elements are in the array by using count. Remove count and use print_r($tobj_arr_fresh)

Using usort in php with a class private function

ok using usort with a function is not so complicated
This is what i had before in my linear code
function merchantSort($a,$b){
return ....// stuff;
}
$array = array('..','..','..');
to sort i simply do
usort($array,"merchantSort");
Now we are upgrading the code and removing all global functions and putting them in their appropriate place. Now all the code is in a class and i can't figure out how to use the usort function to sort the array with the parameter that is an object method instead of a simple function
class ClassName {
...
private function merchantSort($a,$b) {
return ...// the sort
}
public function doSomeWork() {
...
$array = $this->someThingThatReturnAnArray();
usort($array,'$this->merchantSort'); // ??? this is the part i can't figure out
...
}
}
The question is how do i call an object method inside the usort() function
Make your sort function static:
private static function merchantSort($a,$b) {
return ...// the sort
}
And use an array for the second parameter:
$array = $this->someThingThatReturnAnArray();
usort($array, array('ClassName','merchantSort'));
open the manual page http://www.php.net/usort
see that the type for $value_compare_func is callable
click on the linked keyword to reach http://php.net/manual/en/language.types.callable.php
see that the syntax is array($this, 'merchantSort')
You need to pass $this e.g.: usort( $myArray, array( $this, 'mySort' ) );
Full example:
class SimpleClass
{
function getArray( $a ) {
usort( $a, array( $this, 'nameSort' ) ); // pass $this for scope
return $a;
}
private function nameSort( $a, $b )
{
return strcmp( $a, $b );
}
}
$a = ['c','a','b'];
$sc = new SimpleClass();
print_r( $sc->getArray( $a ) );
In this example I am sorting by a field inside the array called AverageVote.
You could include the method inside the call, which means you no longer have the class scope problem, like this...
usort($firstArray, function ($a, $b) {
if ($a['AverageVote'] == $b['AverageVote']) {
return 0;
}
return ($a['AverageVote'] < $b['AverageVote']) ? -1 : 1;
});
In Laravel (5.6) model class, I called it like this, both methods are public static, using php 7.2 on windows 64 bit.
public static function usortCalledFrom()
public static function myFunction()
I did call in usortCalledFrom() like this
usort($array,"static::myFunction")
None of these were work
usort($array,"MyClass::myFunction")
usort($array, array("MyClass","myFunction")
simplest way would be to make an arrow function which calls your sort function like so:
uasort($array, fn($a, $b) => $this->mySortFunction($a, $b, $optionalAdditionalParam))
...
private function mySortFunction($a, $b) {
return -1; // replace with sort logic
}
This worked for me, I hope it helps someone:
usort($array, [ClassName::class, "functionName"]);
Replace "ClassName" with the name of your PHP Class and "functionName" with the name of your private static function.
If everything occurs inside the own class this one works as well:
usort($array, [self::class, "functionName"]);
Replace "functionName" with the name of your private static function.

Categories