Provides the functionality to compare PHP values for equality.

Overview

sebastian/comparator

CI Status Type Coverage

This component provides the functionality to compare PHP values for equality.

Installation

You can add this library as a local, per-project dependency to your project using Composer:

composer require sebastian/comparator

If you only need this library during development, for instance to run your project's test suite, then you should add it as a development-time dependency:

composer require --dev sebastian/comparator

Usage

<?php
use SebastianBergmann\Comparator\Factory;
use SebastianBergmann\Comparator\ComparisonFailure;

$date1 = new DateTime('2013-03-29 04:13:35', new DateTimeZone('America/New_York'));
$date2 = new DateTime('2013-03-29 03:13:35', new DateTimeZone('America/Chicago'));

$factory = new Factory;
$comparator = $factory->getComparatorFor($date1, $date2);

try {
    $comparator->assertEquals($date1, $date2);
    print "Dates match";
} catch (ComparisonFailure $failure) {
    print "Dates don't match";
}
Comments
  • ArrayComparator ignores order - Part 2

    ArrayComparator ignores order - Part 2

    As in https://github.com/sebastianbergmann/comparator/issues/44 mentioned the ArrayComparator ignore the order of two arrays, also if the attribute $canonicalize = false is set.

    opened by panvid 11
  • DateTimeComparator is invalid

    DateTimeComparator is invalid

    1. Required version of PHP moved up, but code not updated yet(partially fixes by PR #95)
    2. DateTimeComparator::assertEquals() calls setTimezone(), but DateTimeInterface not declare this method
    opened by WinterSilence 10
  • Disable escaping of non-latin characters

    Disable escaping of non-latin characters

    This PR mainly solves #21.

    I want to point to a few aspects of my changes:

    1. As you can see, I removed
    $document->formatOutput = true;
    $document->normalizeDocument();
    

    Because tests not failed and I don't see why it needed. I could return it add appropriate tests if you explain me reason of this code. 2. I removed a lot of checks instanceof DOMDocument because in fact there is always DOMDocument in the $node in case if canonicalize is always true. 3. I removed $canonicalize because in fact it is always true and the unused argument just make the code maintenance harder. Especially without corresponding unit-tests. 4. I made nodeToText public to be able to test it. 5. ~~I see the possible error with the $ignoreCase argument (it ignores case when set to false) and I could fix it in this PR or in the different. What do you think?~~ Fixed in the #53

    I'll be happy to receive any criticism/suggestions about the appearance of my changes and tests.

    opened by Ovsyanka 8
  • [PHP 7.2 nightly] DOMNodeComparator::assertEquals() bad method signature

    [PHP 7.2 nightly] DOMNodeComparator::assertEquals() bad method signature

    Hi @sebastianbergmann,

    I hit an issue with the signature of DOMNodeComparator::assertEquals() and PHP 7.2 nightly found by Travis CI, it seems the arguments don't match with ObjectComparator::assertEquals():

    Fatal error: Declaration of
    SebastianBergmann\Comparator\DOMNodeComparator::assertEquals($expected, $actual, $delta = 0, $canonicalize = false, $ignoreCase = false)
    must be compatible with
    SebastianBergmann\Comparator\ObjectComparator::assertEquals($expected, $actual, $delta = 0, $canonicalize = false, $ignoreCase = false, array &$processed = Array)
    in phar:///home/travis/.phpenv/versions/master/bin/phpunit/sebastian-comparator/DOMNodeComparator.php
    on line 0
    

    PHP and Composer versions:

    $ php --version
    PHP 7.2.0-dev (cli) (built: Jan 28 2017 22:46:30) ( ZTS )
    Copyright (c) 1997-2017 The PHP Group
    Zend Engine v3.2.0-dev, Copyright (c) 1998-2017 Zend Technologies
        with Zend OPcache v7.2.0-dev, Copyright (c) 1999-2017, by Zend Technologies
    $ composer --version
    Composer version 1.3.2 2017-01-27 18:23:41
    
    opened by claudiopro 7
  • Fixed the composer requirements

    Fixed the composer requirements

    • Fixed the validity of the composer file
    • Changed constraint to be bound (see https://github.com/composer/composer/pull/2321 for an explanation why it is better)
    opened by stof 7
  • Fix comparison of DateTimeImmutable objects

    Fix comparison of DateTimeImmutable objects

    With the changes made in 408d0d190976d08de44f19a8e502195a423b1281, comparison for DateTimeImmutable objects is broken, as the clone is no longer modified but rather returned as the result of the setTimezone and add calls. This pull requests uses the fluid interface defined for DateTime and DateTimeImmutable to ensure the modified clones are compared.

    While making this change, I discovered indentation issues in the comparator test. If you'd rather not have whitespace fixes along with this pull request, let me know and I'll drop the commit.

    opened by alcaeus 5
  • Fix float comparison precision

    Fix float comparison precision

    needed for https://github.com/sebastianbergmann/phpunit/pull/4972

    fixed EPSILON was problematic and small values like 1E-100 and 1E-200 were considered the same

    this PR removes the DoubleComparator completely, if number is supposed to be compared unexactly, delta can be passed to NumericComparator

    opened by mvorisek 4
  • Array comparator should highlight the difference?

    Array comparator should highlight the difference?

    Thanks for this great library, Sebastian. I'm going to get PHPSpec to use the ArrayComparator. I think I'm missing something because it's not built-in but seems so essential... can't be just me?

    Anyway, there are these lines in ArrayComparator:

    try {
        $comparator = $this->factory->getComparatorFor($value, $actual[$key]);
        $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed);
    
        $expectedAsString .= \sprintf(
                "    %s => %s\n",
                $this->exporter->export($key),
                $this->exporter->shortenedExport($value)
                );
    
        $actualAsString .= \sprintf(
                "    %s => %s\n",
                $this->exporter->export($key),
                $this->exporter->shortenedExport($actual[$key])
                );
    } catch (ComparisonFailure $e) {
        $expectedAsString .= \sprintf(
                "    %s => %s\n",
                $this->exporter->export($key),
                $e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $this->exporter->shortenedExport($e->getExpected())
                );
    
        $actualAsString .= \sprintf(
                "    %s => %s\n",
                $this->exporter->export($key),
                $e->getActualAsString() ? $this->indent($e->getActualAsString()) : $this->exporter->shortenedExport($e->getActual())
                );
    
        $equal = false;
    }
    

    I'm wondering why the second (failure) set of $actualAsString and $expectedAsString assignments aren't decorated so that the output highlights which key/values aren't matching? Something like:

    Array (
        'a' => 'b'
    --->'c' => 'e'
    )
    

    I'm happy to try a PR, but I'm thinking this is too obvious to be an idea that hasn't been considered already...

    stale 
    opened by bitwombat 4
  • Comparison of DateTime and DateTimeImmutable mock objects

    Comparison of DateTime and DateTimeImmutable mock objects

    Fixes:

    • https://github.com/sebastianbergmann/phpunit/issues/3208
    • https://github.com/phpspec/phpspec/issues/887

    Problem:

    In certain scenarios mocking DateTime and DateTimeImmutable instances might be required. Currently any comparison between such mocks will fail because some methods are invoked by the comparator and it comes as a surprise to everyone involved.

    Solution:

    We might mock the methods, however, even if we expect the methods to be called in the mocks, there's no guarantee the implementation won't change in the next version of the library.

    In most cases if we are mocking an object, we would probably be comparing it to the very same instance later on in the unit test. I figured, we could use spl_object_hash() to validate whether the actual and the expected values reference the same instance and skip comparing any other properties if so.

    I wrote a unit test to validate that mocks of both DateTime and DateTimeImmutable pass. I am willing to write one that asserts that a clone of a mock would fail, but am not sure how to catch the error shown.

    Do you think there are drawbacks to this approach? Let me know what your thoughts are.

    opened by dinamic 4
  • Blank diff output of NumericComparator

    Blank diff output of NumericComparator

    The following assertion produces blank output: $this->assertEquals(1, 0);

    Output There was 1 failure:

    1. Test: Run Test Test.php:testRun - Expected | + Actual

    Expected output There was 1 failure:

    1. Test: Run Test Test.php:testRun - Expected | + Actual -1 +0

    ComparisonFailure exception should be provided with expectedAsString and actualAsString values (https://github.com/sebastianbergmann/comparator/blob/master/src/NumericComparator.php#L61) but it isn't. As a result ComparisonFailure::getDiff() produces blank output: https://github.com/sebastianbergmann/comparator/blob/master/src/ComparisonFailure.php#L114

    stale 
    opened by olegrom32 4
  • Support DateTimeInterface in DateTimeComparator

    Support DateTimeInterface in DateTimeComparator

    DateTimeComparator is a great tool for comparing DateTime instances and very helpful in PHPUnit for providing assertEquals functionality for these objects.

    The fact that it compares DT instances based on their actual represented time is great, because that is, what you usually want to compare in your tests (not that the DT instances are actually equal - meaning including the time zone etc.).

    Currently there is one big drawback and that is no support for DateTimeInterface (hence also no support for DateTimeImmutable).

    You can simply do

    <?php
    $this->assertEquals(
      new DateTime('2013-03-29T05:13:35-0600'),
      new DateTime('2013-03-29T05:13:35-0500')
    );
    

    but you cannot do

    <?php
    $this->assertEquals(
      new DateTime('2013-03-29T05:13:35-0600'),
      new DateTimeImmutable('2013-03-29T05:13:35-0500')
    );
    

    I think this should be definitely supported for the sake of consistency and user expectations and this PR provides the support.

    Implementation notes:

    • Since the interfaces are not present before PHP 5.5 the tests were added as separate tests, not in current providers, so that they can be skipped nicely.
    • I had to remove the DateTime typehint in DateTimeComparator::dateTimeToString, because otherwise it would be not compatible and DateTimeInterface cannot be required because of the older versions. I chose not to add any verification of the argument since this is a protected method, if you want me to check the argument and throw an exception (or something else, since there is already an error string returned?), just tell me.
    • I am not sure if the string 'Invalid DateTime object' returned from DateTimeComparator::dateTimeToString should be changed, because someone might consider it part of the interface (for representing error state), but again I think clarity is more important in this case.
    opened by VasekPurchart 4
  • Issue #77: Canonicalizing associative arrays is now done by using ksort instead of sort

    Issue #77: Canonicalizing associative arrays is now done by using ksort instead of sort

    This covers issue #77 which is also the cause for PHPUnit Issues #4455.

    The first change is in "src/ArrayComparator.php" where we first detect if the expected array has any non-integer keys. If that is the case, we treat both arrays as associative and sort them with ksort() instead of sort().

    The second change is test coverage of the above logic in "tests/ArrayComparatorTest.php".

    opened by alecsmrekar 0
  • When C14N fails try comparison without canonicalization

    When C14N fails try comparison without canonicalization

    The issue #87 points out that in some edge cases the C14N can fail. It is not a common case, even psalm don't recognize that c14n() can return false.

    This problem can be reproduced by setting a namespace uri value as a defined xml entity.

    <!DOCTYPE foo SYSTEM "foo.dtd" [
      <!ENTITY ns_foo "http://uri.tld/foo">
    ]>
    <i:foo xmlns:i="&ns_foo;"/>
    

    I append two cases to tests when C14N fails (using current providers):

    • Identicals documents must assert equal
    • Different documents must throw ComparisonFailure

    To solve it, I separate @$document->loadXML($node->C14N()) into two steps: $c14n = @$node->C14N() and @$document->loadXML($c14n). If $c14n === false then retry node to text conversion but without using canonicalization.

    Observations:

    • This is an edge case, C14N failing is very uncommon.
    • DOMNodeComparator::nodeToText is always called with $canonicalize = true, DOMNodeComparator::assertEquals ignored the $canonicalize argument.
    • The comparison on this cases (when C14N fails) is more strict, since canonicalization is not performed.
    • Didn't remove the silent operator, and extended it to c14n() call. This is preserving the same behavior as before.

    If you think is necessary I can try other approaches:

    • Node canonicalization and xml loading capturing libxml errors, then throw an exception. This will require to add another exception that contains the libxml_get_errors() array, or, implode the errors into a single message and use the current SebastianBergmann\Comparator\RuntimException.

    • If convert the node to text fails then throw a ComparisonFailure. This is like saying that $expected is different than $actual because the comparator is unable to perform the comparison.

    • Change DOMNodeComparator::acept() and only return true if objects are DOMNode and the canonicalization don't fail. I don't like this approach because canonicalization can be very expensive.

    opened by eclipxe13 7
  • DOMNodeComparator::nodeToText used C14N but don't do anything when it fail.

    DOMNodeComparator::nodeToText used C14N but don't do anything when it fail.

    I have been experimenting false positives on asserting that two XML are equal on <4.0.1, and TypeError on >=4.0.1. The change on >=4.0.1 is the declare(strict_types=1).

    $xml = <<<EOT
    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
      <!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
    ]>
    <!-- This is a reduction of a much larger SVG file from Adobe Illustrator 15.0.0, SVG Export Plug-In. -->
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:i="&ns_ai;" foo="foo"/>
    EOT;
    
    // just change something to illustrate the problem
    $modified = str_replace('foo="foo"', 'foo="bar"', $xml);
    
    // on < 4.0.1 this is false positive
    $this->assertXmlStringEqualsXmlString($xml, $modified);
    
    // on => 4.0.1 this is a TypeError, but xmls contents are the same
    $this->assertXmlStringEqualsXmlString($xml, $xml);
    

    On >=4.0.1 the declare(strict_types=1) was set on DOMNodeComparator and it raises a TypeError.

    TypeError : DOMDocument::loadXML() expects parameter 1 to be string, bool given
    path-of-my-test.php:99
    

    I trace down the error to DOMNodeComparator::nodeToText where it try to always canonicalize the XML by doing @$document->loadXML($node->C14N());. The problem is when DOMNode::C14N() fails and return false.

    • On <4.0.1 it just try lo load an empty string. In case that both, $actual and $expected fails, then it gives the false positive.

    • On >=4.0.1 because of strict types it throws TypeError, wich is good, but not explicit.

    I think its possible to do something like: the error silence operator: the users of DOMNodeComparator will know that they are providing some object impossible to canonicalize and compare. The failing point will be raised by $node->C14N() returning false.

    • Separate and silence @$node->C14N(), but check the return value on false and fail.

    • Throw an exception that inform what the problem is instead of letting the TypeError to raise.

    Would you like me to take care of this issue with a PR?

    Would you suggest a better approach?

    opened by eclipxe13 7
  • Comporator for Strings

    Comporator for Strings

    I think there need create comporator for strings and move code cor string compare form Scalar to separate class. Then will possible make string comparations with canonicalizing

    Main issue in phpunit: https://github.com/sebastianbergmann/phpunit/issues/4049

    opened by pwsdotru 0
Owner
Sebastian Bergmann
Sebastian Bergmann is the creator of PHPUnit. He co-founded thePHP.cc and helps PHP teams build better software.
Sebastian Bergmann
Compare your Github followers vs following users

followers-vs-following Compare your Github followers vs following users https://docs.github.com/en/rest/reference/users The code only queries 3k follo

Felix Biego 2 Jan 11, 2022
A PHP library to write values to .env (DotEnv) files

DotEnvWriter A PHP library to write values to .env (DotEnv) files Installation DotEnvWriter can be installed with Composer. composer require mirazmac/

Miraz Mac 9 May 24, 2022
Sanitize and escape every values in your PHP Application

PHP Sanitizer Sanitize and escape every values in your PHP Application. This solution will make PHP developer life easy, very easy and developers woul

Maniruzzaman Akash 8 Aug 23, 2022
JSONFinder - a library that can find json values in a mixed text or html documents, can filter and search the json tree, and converts php objects to json without 'ext-json' extension.

JSONFinder - a library that can find json values in a mixed text or html documents, can filter and search the json tree, and converts php objects to json without 'ext-json' extension.

Eboubaker Eboubaker 2 Jul 31, 2022
Humanize values that are readable only for developers.

PHP Humanizer Tests - 4.x Readme for 4.x version Humanize values to make them readable for regular people ;) Installation Run the following command: c

Coduo 1.5k Aug 30, 2022
Google-like values converter

Google-like values converter. Support for different types of conversions, for examples: 1 kilometer -> meters 1 dollar -> THB 1 kilogram -> meters ...

Pavinthan 1 Nov 4, 2021
Composer plugin replacing placeholders in the scripts section by dynamic values

Composer Substitution Plugin The Composer Substitution plugin replaces placeholders in the scripts section by dynamic values. It also permits to cache

Fabien Villepinte 49 Jan 8, 2022
Read and show values from form valid

read-and-show-values-from-form-valid Escribe un programa PHP que permita al usuario rellenar un formulario de registro con los datos de nombre, contra

ManuMT 1 Jan 21, 2022
Miniset allows you to create compact sets of fields that either combine into a string of classes, or return a simple array of values

Miniset allows you to create compact sets of fields that either combine into a string of classes, or return a simple array of values. Miniset

Jack Sleight 5 Jun 13, 2022
laminas-xml2json provides functionality for converting XML structures to JSON

laminas-xml2json This package is considered feature-complete, and is now in security-only maintenance mode, following a decision by the Technical Stee

Laminas Project 12 Aug 9, 2022
PHP library providing retry functionality with multiple backoff strategies and jitter support

PHP Backoff Easily wrap your code with retry functionality. This library provides: 4 backoff strategies (plus the ability to use your own) Optional ji

Signature Tech Studio 135 Sep 12, 2022
Incubator adapters/functionality for the Phalcon PHP Framework

Phalcon Incubator This is a repository to publish/share/experiment with new adapters, prototypes or functionality that can potentially be incorporated

The Phalcon PHP Framework 736 Sep 19, 2022
Analyzer of PHP code to search issues with deprecated functionality in newer interpreter versions.

PhpDeprecationDetector PhpDeprecationDetector - analyzer of PHP code to search usages of deprecated functionality in newer interpreter versions - depr

Sergey 298 Sep 21, 2022
Analyzer of PHP code to search issues with deprecated functionality in newer interpreter versions.

PhpDeprecationDetector PhpDeprecationDetector - analyzer of PHP code to search usages of deprecated functionality in newer interpreter versions - depr

Sergey 298 Sep 21, 2022
Tiny PHP library providing retry functionality with multiple backoff strategies and jitter support

JBZoo / Retry 4 retry strategies (plus the ability to use your own) Optional jitter / randomness to spread out retries and minimize collisions Wait ti

JBZoo Toolbox 4 Sep 8, 2022
A project to add vanilla functionality into PocketMine-MP (4.0)

libVanilla A project to add vanilla functionality into PMMP. As this project was created specifically to support anvils/enchanting, those are the only

Valiant Network 10 Mar 31, 2022
Extends the customer functionality of Magento.

FireGento_Customer This extension extends the core functionality of the customer module of Magento. It is possible to temporarily lock the user accoun

FireGento e. V. 23 Apr 1, 2022
This plugin adds 95% of 1.16 blocks & items and their functionality

INether This plugin adds 95% of 1.16 blocks & items and their functionality Implemented blocks Ancient Debris All types of Basalt Crimson & Warped Fun

null 32 Aug 26, 2022
Laravel Blog Package. Easiest way to add a blog to your Laravel website. A package which adds wordpress functionality to your website and is compatible with laravel 8.

Laravel Blog Have you worked with Wordpress? Developers call this package wordpress-like laravel blog. Give our package a Star to support us ⭐ ?? Inst

Binshops 268 Sep 21, 2022