0

我在我的项目中使用PHP-Parser 。我想使用PHPUnit'sassertEquals函数比较两个节点。

尽管节点相同,但它给出了错误的结果。原因是,其中一个节点包含两个受保护的属性,而另一个不包含:

["attributes":protected]=>
array(2) {
  ["startLine"]=>
  int(2)
  ["endLine"]=>
  int(2)
}

是否可以比较不包括受保护属性的节点?


示例数据

第一个对象:

array(1) {
  [0]=>
  object(PhpParser\Node\Stmt\Expression)#5924 (2) {
    ["expr"]=>
    object(PhpParser\Node\Expr\Assign)#5923 (3) {
      ["var"]=>
      object(PhpParser\Node\Expr\Variable)#5918 (2) {
        ["name"]=>
        string(1) "x"
        ["attributes":protected]=>
        array(2) {
          ["startLine"]=>
          int(2)
          ["endLine"]=>
          int(2)
        }
      }
      ["expr"]=>
      object(PhpParser\Node\Expr\ArrayDimFetch)#5922 (3) {
        ["var"]=>
        object(PhpParser\Node\Expr\Variable)#5919 (2) {
          ["name"]=>
          string(3) "arr"
          ["attributes":protected]=>
          array(2) {
            ["startLine"]=>
            int(2)
            ["endLine"]=>
            int(2)
          }
        }
        ["dim"]=>
        object(PhpParser\Node\Scalar\String_)#5934 (2) {
          ["value"]=>
          string(3) "FOO"
          ["attributes":protected]=>
          array(0) {
          }
        }
        ["attributes":protected]=>
        array(2) {
          ["startLine"]=>
          int(2)
          ["endLine"]=>
          int(2)
        }
      }
      ["attributes":protected]=>
      array(2) {
        ["startLine"]=>
        int(2)
        ["endLine"]=>
        int(2)
      }
    }
    ["attributes":protected]=>
    array(2) {
      ["startLine"]=>
      int(2)
      ["endLine"]=>
      int(2)
    }
  }
}

第二个对象:

array(1) {
  [0]=>
  object(PhpParser\Node\Stmt\Expression)#5930 (2) {
    ["expr"]=>
    object(PhpParser\Node\Expr\Assign)#5929 (3) {
      ["var"]=>
      object(PhpParser\Node\Expr\Variable)#250 (2) {
        ["name"]=>
        string(1) "x"
        ["attributes":protected]=>
        array(2) {
          ["startLine"]=>
          int(2)
          ["endLine"]=>
          int(2)
        }
      }
      ["expr"]=>
      object(PhpParser\Node\Expr\ArrayDimFetch)#5928 (3) {
        ["var"]=>
        object(PhpParser\Node\Expr\Variable)#5926 (2) {
          ["name"]=>
          string(3) "arr"
          ["attributes":protected]=>
          array(2) {
            ["startLine"]=>
            int(2)
            ["endLine"]=>
            int(2)
          }
        }
        ["dim"]=>
        object(PhpParser\Node\Scalar\String_)#5927 (2) {
          ["value"]=>
          string(3) "FOO"
          ["attributes":protected]=>
          array(3) {
            ["startLine"]=>
            int(2)
            ["endLine"]=>
            int(2)
            ["kind"]=>
            int(1)
          }
        }
        ["attributes":protected]=>
        array(2) {
          ["startLine"]=>
          int(2)
          ["endLine"]=>
          int(2)
        }
      }
      ["attributes":protected]=>
      array(2) {
        ["startLine"]=>
        int(2)
        ["endLine"]=>
        int(2)
      }
    }
    ["attributes":protected]=>
    array(2) {
      ["startLine"]=>
      int(2)
      ["endLine"]=>
      int(2)
    }
  }
}

注意PhpParser\Node\Scalar\String_对象["value"]=> string(3) "FOO"

4

3 回答 3

0

我无法用你的确切数据对此进行测试,但这个概念应该有效。

/**
 * Test if two variables are the same, excluding protected properties of objects.
 * @return boolean
 */
function compare_public($a, $b) {
    // If $a and $b are different things, they're not the same :)
    if (gettype($a) != gettype($b)) {
        return false;
    }
    if (is_array($a)) {
        // If $a and $b have different lengths, they're not the same.
        if (count($a) != count($b)) {
            return false;
        }
        // Call this function recursively to compare each element of $a and $b.
        // If any returns false, $a and $b are not the same.
        return count(array_filter(array_map(compare_public, $a, $b))) == count($a);
    } elseif (is_object($a)) {
        // If $a and $b are different classes, they're not the same.
        if (get_class($a) != get_class($b)) {
            return false;
        }
        // Use reflection to find all the public properties and compare them.
        // Return false if any property is different.
        $c = new ReflectionClass(get_class($a));
        foreach ($c->getProperties(ReflectionProperty::IS_PUBLIC) as $p) {
            if (!compare_public($a->{$p->name}, $b->{$p->name})) { return false; }
        }
        // All the properties matched. Return true.
        return true;
    } else {
        // Straightforward comparison for non-array, non-objects.
        return $a === $b;
    }
}

这是一个人为的例子。

class TestObject
{
    public $a;
    public $b;
    protected $c;

    public function __construct($a, $b, $c)
    {
        $this->a = $a; $this->b = $b; $this->c = $c;
    }
}
//                                             V      V    <- protected properties
$first  = [new TestObject(new TestObject(1, 2, 3), 2, 3)];
$second = [new TestObject(new TestObject(1, 2, 4), 2, 4)];

var_dump($first, $second, compare_public($first, $second));

输出:

array(1) {
  [0]=>
  object(TestObject)#1 (3) {
    ["a"]=>
    object(TestObject)#2 (3) {
      ["a"]=>
      int(1)
      ["b"]=>
      int(2)
      ["c":protected]=>
      int(3)
    }
    ["b"]=>
    int(2)
    ["c":protected]=>
    int(3)
  }
}
array(1) {
  [0]=>
  object(TestObject)#3 (3) {
    ["a"]=>
    object(TestObject)#4 (3) {
      ["a"]=>
      int(1)
      ["b"]=>
      int(2)
      ["c":protected]=>
      int(4)
    }
    ["b"]=>
    int(2)
    ["c":protected]=>
    int(4)
  }
}
bool(true)
于 2019-08-19T21:23:52.213 回答
0

您可以使用节点遍历器删除该属性。请参阅此处的文档。

你的节点遍历器看起来像这样,

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class MyNodeVisitor extends NodeVisitorAbstract
{
    public function leaveNode(Node $node) {
        // You might want to do additional checks here
        $node->setAttributes([]);
    }
}

这将删除 PHP-Parser 设置的所有受保护属性。

尽管正如其中一条评论所暗示的那样,您将无法使用 PHPUnit 断言来比较节点,因为这两个节点实例并不相同。但是您可能可以为您的测试编写一个自定义断言。

于 2019-08-20T15:36:20.227 回答
0

对象的实例总是不相等的。您可以像其他答案一样编写比较函数,但简单地编码为 JSON 将隐藏所有受保护的属性并保留公共属性。然后比较字符串就足够了。

这里有一个例子

http://sandbox.onlinephpfunctions.com/code/174b9bf5317a200dd42a83c082d3c95558baae90

于 2019-08-15T02:05:36.980 回答