Cannot do OOP Callback function for usort

2019-08-09 08:25发布

I have read through a few dozen discussion of the use of class members as callback functions but none of the examples address what seems to me to be the obvious OOP design, based upon working with other OOP languages such as C++ and Java.

I have defined a comparison method within the class:

class TestClass {
private $aField; // integer value
function _construct($value)
{
        $this->aField   = $value;
}       // TestClass::_construct

function compare($other)
{
    if ($other instanceof TestClass)
    {           // comparing two instances
    return $this->afield - $other->afield;
    }           // comparing two events
    else
    throw new Exception("parameter is not instance of TestClass");
}       // TestClass::compare
}       // class TestClass

$instances  = array(new TestClass(5),new TestClass(3));
// the following fails because:
// 1. $this is not defined outside a class member
// 2. compare does not take two parameters
usort($instances, array($this, 'compare'));

// the following kluge works around this
function order($this, $that) { return $this->compare($that); }
usort($instances, 'order');

All of the examples I can find on this forum and in the PHP documentation are not complete in that they do not show the context of the call to usort. Now I can get around this by making the comparison function a static function with two parameters and invoke it as a callable by array('TestClass','compare'), but it is not intuitive to define a comparison method as a static function of the class. Based upon over 30 years of personal experience in OOP static functions are in most cases a result of poor class design. In particular static methods defeat polymorphism.

What I am looking for is a sort function like that of Java, which exploits the Comparable interface. I see that there has been some discussion dating back to 2010 about defining a Comparable interface, but that discussion is only in connection with the comparison operators, and makes PHP purists uncomfortable because it overloads operators. However the Java Comparable interface does not overload operators since, with the glaring exception of the String class, Java does not support overloading operators. Implementing the Comparable interface in Java enables you to use the class in SortedMaps and to sort instances within a Collection, but if you wish to compare two objects you must explicitly call the compareTo method. I also see that there is an essentially undocumented compare_objects method which already overrides the default implementation of the comparison operators. In other words despite the purists' objections PHP already permits you to overload the comparison operators for a class. However it is undocumented whether or not the PHP sort function uses compare_objects when it is asked to sort objects.

标签: php oop
2条回答
对你真心纯属浪费
2楼-- · 2019-08-09 08:53

You mentioned a collection class in your comments – so here’s an example using (a very rudimentary) one, combined with the method of sorting taken from the user comment in the manual that I mentioned:

class Collection {
  private $itemType;
  private $items;

  public function __construct($itemType) {
    $this->itemType = $itemType;
    $this->items = [];
  }

  public function add($item) {
    if($item instanceof $this->itemType) {
      $this->items[] = $item;
    }
    else {
      throw new Exception('Only items of Type ' . $this->itemType . ' can be added!');
    }
  }

  public function sort() {
    usort($this->items, array($this, 'compareItems'));

    var_dump($this->items); // test output, to check the result
  }

  private function compareItems($a, $b) { // calls the compare method of the item class
    return $a->compare($b);
  }
}


class TestClass {
  private $aField;

  public function __construct($value) {
    $this->aField = $value;
  }

  public function compare($other) {
    if($other instanceof TestClass) {
      return $this->aField - $other->aField;
    }
    else {
      throw new Exception('Parameter is not instance of TestClass!');
    }
  }
}

$collection = new Collection('TestClass');

$collection->add(new TestClass(5));
$collection->add(new TestClass(1));
$collection->add(new TestClass(3));

$collection->sort();
查看更多
Rolldiameter
3楼-- · 2019-08-09 09:00

You're calling $this outside of your class.

$obj = new TestClass();
$sorted = usort($instances, array($obj, 'compare'));

You could also do something like this (didn't test the code)

class TestClass() {
    function compareWith($other) {
        $arr = array($this, $other);
        usort($arr, function($that, $other) {
            if ($other instanceof TestClass)
            {           // comparing two instances
            return $other->afield - $that->afield;
            }           // comparing two events
            else
                throw new Exception("parameter is not instance of TestClass");
        });
        return $arr;
    }
}

$instance1  = new TestClass();
$instance2  = new TestClass();
$sorted = $instance1->compareWith($instance2);
查看更多
登录 后发表回答