PHP: compareTo similar to Java - php

In an effort to utilize OOP in PHP, I was looking for a way to define functions within objects to use for sorting. in Java this is simple, as the compareTo() is automatically called by sorted arrays and such. I looked around PHP.net and couldn't find anything similar in PHP. I have resorted to what seems like a hack to me, so I'm wondering if anyone can lend some insight on an easier way.
CURRENT CODE:
class SomeObject {
private $someField;
function getSomeField() { return $this->someField; }
function compareTo($other) {
return $this->someField - $other->getSomeField();
}
}
//in funcs.php
function objSort($a, $b) {
return $a->compareTo($b);
}
//To sort an array
usort($array, objSort);
Is there an easier/more sleek way of doing this in PHP?

You could use anonymous function directly.
class SomeObject {
private $someField;
function getSomeField() { return $this->someField; }
}
usort($array, function($a, $b) {
return $a->getSomeField() - $b->getSomeField();
});
Or you could define a static function use to compare.
class SomeObject {
private $someField;
function getSomeField() { return $this->someField; }
static function compare($a, $b) {
return $a->getSomeField() - $b->getSomeField();
}
}
usort($array, array('SomeObject', 'compare'));

I don't think you can do this in PHP, although I would love to be proved wrong. I usually have a package of utility classes for arrays, strings, numbers, etc. with static methods, but it's only useful if you are defining functions that PHP doesn't already have, as you are otherwise just making pointless wrappers for native functions.

Related

PHP usort comparator function needs access to class member variables - how to call it

I am trying to use the usort() function in PHP. I am not sure how to call the comparison function. Here is my code below. I tried $this->comparator and that didnt help. This would have been easy if comparator was a function that didn't need to access the class's member variables.
class A {
$p1 // non-associative array
$p2 // non-associative array
public function comparator($a, $b)
{
// the usual comparison stuff
if ($this->p1[$a] == $this->p2[$b])
return 0;
else ($this->p1[$a] < $this->p2[$b])
return 1;
else
return -1;
}
public function sorting()
{
// after some code
$some_array = array(..);
usort($some_array, "comparator") // <--- ERROR here: does not recognize comparator
}
}
You need to specify the sort function in a callable way:
usort($some_array, array($this, "comparator"));
Even though the type callable didn't exist prior to PHP 5.4, referencing methods works the same way.

PHP modify code to avoid anonymous functions

I've found some solutions to a sorting problem I've been having, but the code uses anonymous functions in PHP. Im using version 5.2.17 and I believe anonymous functions are not supported.
How would I change the following blocks of code so I can use them in PHP 5.2.17
$keys = array_flip($order);
usort($items, function($a, $b) use($keys)
{
return $keys[$a['id']] - $keys[$b['id']];
});
from PHP sort multidimensional array by other array
And
$sorted = array_map(function($v) use ($data) {
return $data[$v - 1];
}, $order);
from PHP - Sort multi-dimensional array by another array
UPDATE:
One of the problems is I'm not sure how the variables $a, $b and $v are used. So I'm not sure how to create normal functions, thus bypassing the anon functions.
Both of the anonymous functions make use of the use clause to pass variables into the local scope.
You can achieve the same with object methods in which the objects have those variables as properties.
Inside the object method you then can access these.
$sorted = array_map(function($v) use ($data) {
return $data[$v - 1];
}, $order);
An exemplary mapping object then could look like:
class MapObj
{
private $data;
public function __construct($data) {
$this->data = $data;
}
public function callback($v) {
return $this->data[$v - 1];
}
}
As you can see it has the same functionality but just written in PHP 5.2 syntax.
And it's usage:
$map = new MapObj($data);
$callback = array($map, 'callback');
$sorted = array_map($callback, $order);
And that's how it works. Callbacks for object methods are always written in form of an array with two members, the first one is the object instance, and the second one is the name of the object method.
Naturally you can extend this an put the mapping function into the mapping object, so it's more straight forward:
class MapObj
{
...
public function map(array $order) {
$callback = array($this, 'callback');
return array_map($callback, $order);
}
}
New usage:
$map = new MapObj($data);
$sorted = $map->map($order);
As you can see this might make the usage a bit more straight forward. I must admit, my method naming is not really brilliant here, so I leave some room for your improvements.
Another benefit is, you can make the visibility of the callback method private then.
The situation with passing the data to work with in the callback as a parameter to the mapping function. That is because you wrote you already have a class that you want to make use of, but you can not touch the constructor. So the given example is a bit short.
Here is another example without using the constructor, I removed it:
class MyObj
{
private $data;
private function callback($v) {
return $this->data[$v - 1];
}
public function map($order, $data) {
$callback = array($this, 'callback');
$this->data = $data;
return array_map($callback, $order);
}
}
As you can see, the constructor is not needed any longer to pass the $data, but instead it's just passed into the map() method as an additional parameter. Usage:
$myObj = new MyObj(....); // somewhere.
// later on:
$myObj->map($order, $data);
// could be also:
$this->map($order, $data);
As you can see, how you set the private member variable is up to you. Do what fits the job.
What you have here is a closure over $data -- it's not 100% possible to rewrite it without an anonymous function. Here's the closest possible approximation:
function _array_sort_callback($a, $b) {
global $_array_sort_callback__keys;
return $_array_sort_callback__keys[$a['id']] - $_array_sort_callback__keys[$b['id']];
}
... {
$keys = array_flip($order);
global $_array_sort_callback__keys;
$_array_sort_callback__keys = $keys;
usort($items, "_array_sort_callback");
}
Note that I've prefixed the name of the global to try to avoid a collision. Both the function name and the global name need to be unique within your application.
Also, keep in mind that PHP 5.2.17 is obsolete and unsupported. You should migrate off of it as soon as possible.
If you want to mimic a closure, where you snapshot variables at a specific time, you can use a simple base class to serve as a container for the values, and then just define sub classes to implement the comparison logic.
Untested
// base class exists purely to capture the value of some variables at instantiation time
// kinda like a closure
class VarCapture {
protected $vars;
function __construct($vars) {
$this->vars = $vars;
}
}
class ItemComparer extends VarCapture {
function compare($a, $b) {
$keys = $this->vars['keys'];
return $keys[$a['id']] - $keys[$b['id']];
}
}
class PluckMapper extends VarCapture {
function pluck($v) {
$data = $this->vars['data'];
return $data[$v - 1];
}
}
$keys = array_flip($order);
$ic = new ItemComparer(compact('keys'));
$callable = array($ic, 'compare');
usort($items, $callable);
$pm = new PluckMapper(compact('data'));
$callable = array($mp, 'pluck');
$sorted = array_map($callable, $order);
Note that I made use of php's callback psuedo type
http://php.net/manual/en/language.types.callable.php
You can also re-write it into pre-5.3 anonymous functions, a la create_function(). Although create_function() functions don't normally act as closures, you can use some tricks (without using global variables) to make them work as closures in some limited circumstances. You encode the closed-over variables directly into the source of the function. The limitations are that data only goes one-way -- in; closed-over variables can only be "simple" data types, like numbers, strings, and arrays; and functions created with create_function are never deallocated, leaking memory; plus it is not very efficient. But I think it is sufficient for your example (assuming you only use arrays and strings and such).
$keys = array_flip($order);
usort($items, create_function('$a,$b', '$keys = '.var_export($keys,true).';
return $keys[$a["id"]] - $keys[$b["id"]];
'));
and
$sorted = array_map(create_function('$v', '$data = '.var_export($data,true).';
return $data[$v - 1];
'), $order);
The more general pre-5.3 solution, though, is to use an object and method as the closure, as in hakra's answer.

Availability of method vars within a sub-function

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.

php - Can I integrate functions with same content, different names?

I have a couple of functions inside a class that essentially do the same thing:
public function fn_a(){
return __FUNCTION__;
}
public function fn_b(){
return __FUNCTION__;
}
public function fn_c(){
return __FUNCTION__;
}
I need those functions to remain in their current names so I intentionally did not do:
public function fn_($letter){
return __FUNCTION__.$letter;
}
I was hoping for some sort of way to minify the verboseness of code here, since they all do the same. The ultimate situation would be something like this:
public functions fn_a, fn_b, fn_c() {
return __FUNCTION__;
}
Another solution, if applicable, might be doing something like Class's "extends":
fn_b, fn_c extend fn_a?
What do you think guys?
Any syntax like the one you suggested is not possible : if you want several distinct functions, you have to declare all those functions.
Still, a possibility could be that your fn_a, fn_b and fn_c functions just be simple wrappers arround a more complex one :
public function fn_a(){
return fn_all('a');
}
public function fn_b(){
return fn_all('b');
}
public function fn_c(){
return fn_all('c');
}
public function fn_all($name) {
// a lot of complex stuff here
}
With that, depending on the length on the fn_all function, of course, you would reduce the amount of code-duplication.
Another idea (not sure how this could be done with methods, so I'll demonstrate with functions) would be to use Closures -- which means PHP >= 5.3
The basic idea being that you'd have a first function, that would return another one -- which would bind the parameter passed to the first one :
First, the function that creates the others :
function creator($name) {
return function () use ($name) {
return $name;
};
}
And, then, let's get three functions, using that creator one :
$fn_a = creator('a');
$fn_b = creator('b');
$fn_c = creator('c');
And now, calling those three functions :
echo $fn_a() . '<br />';
echo $fn_b() . '<br />';
echo $fn_c() . '<br />';
We get the following output :
a
b
c
I've never good at explaining how anonymous functions and closures work -- but searching for "closure" on google should help you understand ; note that you can read tutorial about closures in Javascript : the idea is exactly the same.
(And, as closures are new in PHP -- arrived with PHP 5.3 -- you will not find as many tutorials as for Javascript)
public function fn_catcher($letter) {
return __FUNCTION__.$letter;
}
public function __call($name) {
if (substr($name, 0, 3) == 'fn_')
{
return $this->fn_catcher($name);
}
}
Like that?

Handle requests to several classes within the same PHP SOAP server

Is that possible to have a single PHP SOAP server which will handle requests to several classes (services)?
If yes, could you please show an example implementation?
If not, could you please describe why?
Could you wrap the other services in a single class? Completely untested, it was just a thought...
class MySoapService
{
public function __construct()
{
$this->_service1 = new Service1();
$this->_service2 = new Service2();
}
// You could probably use __call() here and intercept any calls,
// thus avoiding the need for these declarations in the wrapper class...
public function add($a, $b)
{
return $this->_service1->add($a, $b);
}
public function sub($a, $b)
{
return $this->_service2->sub($a, $b);
}
}
class Service1
{
public function add($a, $b)
{
return $a + $b;
}
}
class Service2
{
public function sub($a, $b)
{
return $a - $b;
}
}
Another take on the same general idea (proxy class) - for php5
Uses a hash to map functions to callbacks.
class ServiceProxy {
private $map = array();
public function addMethod($name, $callback) {
if(is_callable($callback)) {
$this->map[$name] = $callback;
return true;
}
return false;
}
function __call($name, $args) {
if(isset($map[$name])) {
return call_user_func_array($map[$name], $args);
} else {
return null;
}
}
}
This class could also use the reflection API and add all pulic methods from an object, for example.
If both classes had methods with identical names (but different parameters) then you could use func_get_args() to analyze the arguments and differentiate between the methods that way.
If they take the same arguments... then you're kinda stuck.
Why can't you just use two separate Web Services?

Categories