diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..af8f9b2 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,49 @@ +# PHP CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-php/ for more details +# +version: 2 +jobs: + build: + docker: + # Specify the version you desire here + - image: circleci/php:7.4-apache + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + # Using the RAM variation mitigates I/O contention + # for database intensive operations. + # - image: circleci/mysql:5.7-ram + # + # - image: redis:2.8.19 + + steps: + - checkout + + - run: sudo apt-get update -y # PHP CircleCI 2.0 Configuration File# PHP CircleCI 2.0 Configuration File sudo apt install zlib1g-dev libsqlite3-dev + - run: sudo docker-php-ext-install zip + + # Download and cache dependencies + - restore_cache: + keys: + # "composer.lock" can be used if it is committed to the repo + - v1-dependencies-{{ checksum "composer.json" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + + - run: composer selfupdate && composer install -n --prefer-dist + + - save_cache: + key: v1-dependencies-{{ checksum "composer.json" }} + paths: + - ./vendor + + # prepare the database + # - run: touch storage/testing.sqlite + # - run: php artisan migrate --env=testing --database=sqlite_testing --force + + # run tests with phpunit or codecept + - run: ./vendor/bin/phpunit + # - run: ./vendor/bin/codecept build + # - run: ./vendor/bin/codecept run diff --git a/.gitignore b/.gitignore index 8b7ef35..cf8e345 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ /vendor composer.lock +*.cache +clover.xml +junit-logfile.xml diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..8032396 --- /dev/null +++ b/.php_cs @@ -0,0 +1,17 @@ +notPath('vendor') + ->in(__DIR__) + ->name('*.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +return PhpCsFixer\Config::create() + ->setRules([ + '@PSR2' => true, + 'ordered_imports' => ['sortAlgorithm' => 'alpha'], + 'no_unused_imports' => true, + 'psr4' => true, + ]) + ->setFinder($finder); diff --git a/.travis.yml b/.travis.yml index eae84cc..c28ea00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,20 +1,25 @@ +env: + global: + - COMPOSER_MEMORY_LIMIT=-1 + - XDEBUG_MODE=coverage + language: php php: - - 5.5 - 5.6 - 7.0 - 7.1 + - 7.2 + - 7.3 + - 7.4 + - nightly cache: directories: - $HOME/.composer/cache -before_install: - - sudo apt-get update -qq - before_script: - - curl -s http://getcomposer.org/installer | php - - php -n composer.phar install --dev --verbose + - composer selfupdate + - composer install -script: phpunit +script: vendor/bin/phpunit diff --git a/README.md b/README.md index dbba18f..ba17e63 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ [![Build status](https://ci.appveyor.com/api/projects/status/wues2pcnb6s07bbl?svg=true)](https://ci.appveyor.com/project/roger-vila/array-diff-multidimensional) [![Code Climate](https://codeclimate.com/github/rogervila/array-diff-multidimensional/badges/gpa.svg)](https://codeclimate.com/github/rogervila/array-diff-multidimensional) [![StyleCI](https://styleci.io/repos/82589676/shield?branch=master)](https://styleci.io/repos/82589676) +[![Total Downloads](https://img.shields.io/packagist/dt/rogervila/array-diff-multidimensional)](https://packagist.org/packages/rogervila/array-diff-multidimensional) +[![Latest Stable Version](https://img.shields.io/packagist/v/rogervila/array-diff-multidimensional)](https://packagist.org/packages/rogervila/array-diff-multidimensional) +[![License](https://img.shields.io/packagist/l/rogervila/array-diff-multidimensional)](https://packagist.org/packages/rogervila/array-diff-multidimensional) + [![SensioLabsInsight](https://insight.sensiolabs.com/projects/0d8faa82-5cd3-44dd-9759-a8a1b7b55fce/big.png)](https://insight.sensiolabs.com/projects/0d8faa82-5cd3-44dd-9759-a8a1b7b55fce) Works like the [PHP array_diff()](http://php.net/manual/es/function.array-diff.php) function, but with multidimensional arrays. @@ -37,7 +41,7 @@ $old = [ ], ]; -var_dump(ArrayDiffMultidimensional::compare($new,$old)); +var_dump(ArrayDiffMultidimensional::compare($new, $old)); ``` @@ -51,6 +55,27 @@ The result of comparing `$new` with `$old` will return a new array with the chan ] ``` +## Strict vs Loose comparisons + +**Comparisons are strict by default**, but you can specify that you want to do a loose comparison passing a boolean as a third parameter for `compare` method, or calling the `looseComparison` + +```php +// This will deactivate the strict comparison mode +ArrayDiffMultidimensional::compare($new, $old, false); + +// This method call is equivalent +ArrayDiffMultidimensional::looseComparison($new, $old); +``` + +Also, a `strictComparison` method is available for more clarity +```php +// Comparisons are strict by default +ArrayDiffMultidimensional::compare($new, $old); + +// This method call is equivalent +ArrayDiffMultidimensional::strictComparison($new, $old); +``` + ## License Array Diff Multidimensional is an open-sourced package licensed under the [MIT license](http://opensource.org/licenses/MIT). diff --git a/appveyor.yml b/appveyor.yml index d7d3c9b..8e4cd15 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,45 +1,51 @@ build: false - -platform: x86 - -clone_folder: c:\projects\arraydiffmultidimensional +version: appveyor-{branch}-{build} +shallow_clone: false +clone_folder: C:\projects\app environment: matrix: - - PHP_DOWNLOAD_FILE: php-5.5.9-nts-Win32-VC11-x86.zip - - PHP_DOWNLOAD_FILE: php-5.6.4-nts-Win32-VC11-x86.zip - - PHP_DOWNLOAD_FILE: php-7.0.0-nts-Win32-VC14-x86.zip - - PHP_DOWNLOAD_FILE: php-7.1.0-nts-Win32-VC14-x86.zip + - php_ver: 8.0.0 + - php_ver: 7.4.13 + - php_ver: 7.3.25 + - php_ver: 7.2.34 + - php_ver: 7.1.33 + - php_ver: 7.0.33 + - php_ver: 5.6.40 + - php_ver: 5.5.38 + # - php_ver: 5.4.45 + # - php_ver: 5.3.29 + +cache: + - '%APPDATA%\Composer' + - '%LOCALAPPDATA%\Composer' + - C:\tools\php -> .appveyor.yml + - C:\tools\composer.phar -> .appveyor.yml init: - - SET PATH=c:\php;%PATH% - - SET COMPOSER_NO_INTERACTION=1 - - SET PHP=1 + - SET PATH=C:\tools\php;%PATH% install: - - IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php) - - cd c:\php - - IF %PHP%==1 appveyor DownloadFile https://raw.githubusercontent.com/symfony/binary-utils/master/cacert.pem - - IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/%PHP_DOWNLOAD_FILE% - - IF %PHP%==1 7z x %PHP_DOWNLOAD_FILE% -y >nul - - IF %PHP%==1 del /Q *.zip - - IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat - - IF %PHP%==1 copy /Y php.ini-development php.ini - - IF %PHP%==1 echo max_execution_time=1200 >> php.ini - - IF %PHP%==1 echo date.timezone="UTC" >> php.ini - - IF %PHP%==1 echo extension_dir=ext >> php.ini - - IF %PHP%==1 echo extension=php_curl.dll >> php.ini - - IF %PHP%==1 echo extension=php_openssl.dll >> php.ini - - IF %PHP%==1 echo extension=php_intl.dll >> php.ini - - IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini - - IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini - - IF %PHP%==1 echo extension=php_pdo_sqlite.dll >> php.ini - - IF %PHP%==1 echo curl.cainfo=c:\php\cacert.pem >> php.ini - - appveyor DownloadFile https://getcomposer.org/composer.phar - - cd c:\projects\arraydiffmultidimensional - - mkdir %APPDATA%\Composer - - composer update --prefer-dist --no-progress --ansi + - ps: Set-Service wuauserv -StartupType Manual + - IF NOT EXIST C:\tools\php (choco install --yes --allow-empty-checksums php --version %php_ver% --params '/InstallDir:C:\tools\php') + - cd C:\tools\php + - copy php.ini-production php.ini + - echo date.timezone="UTC" >> php.ini + - echo memory_limit=512M >> php.ini + - echo extension_dir=ext >> php.ini + - echo extension=php_curl.dll >> php.ini + - echo extension=php_openssl.dll >> php.ini + - echo extension=php_mbstring.dll >> php.ini + - IF NOT EXIST C:\tools\composer.phar (cd C:\tools && appveyor DownloadFile https://getcomposer.org/composer.phar) + - php C:\tools\composer.phar --version + - cd C:\projects\app + +before_test: + - cd C:\projects\app + - php C:\tools\composer.phar selfupdate + - php C:\tools\composer.phar update --optimize-autoloader --no-interaction --no-progress --prefer-stable --no-ansi + - php C:\tools\composer.phar info -D | sort test_script: - - cd c:\projects\arraydiffmultidimensional - - vendor\bin\phpunit.bat --verbose + - cd C:\projects\app + - vendor\bin\phpunit diff --git a/composer.json b/composer.json index d28f7ef..de03cb3 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,6 @@ } }, "require-dev": { - "phpunit/phpunit": "^4.8 || ^5.0" + "phpunit/phpunit": "^4.8 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" } } diff --git a/phpunit.xml b/phpunit.xml index a40a5f4..6989de4 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,20 +1,17 @@ - + + ./tests/ + + + ./src + + + + + + diff --git a/src/ArrayDiffMultidimensional.php b/src/ArrayDiffMultidimensional.php index 869bea3..a011ab7 100644 --- a/src/ArrayDiffMultidimensional.php +++ b/src/ArrayDiffMultidimensional.php @@ -4,28 +4,38 @@ class ArrayDiffMultidimensional { - /** * Returns an array with the differences between $array1 and $array2 + * $strict variable defines if comparison must be strict or not * * @param array $array1 * @param array $array2 + * @param bool $strict + * * @return array */ - public static function compare($array1, $array2) + public static function compare($array1, $array2, $strict = true) { + if (!is_array($array1)) { + throw new \InvalidArgumentException('array1 must be an array!'); + } + + if (!is_array($array2)) { + return $array1; + } + $result = array(); foreach ($array1 as $key => $value) { - if (!is_array($array2) || !array_key_exists($key, $array2)) { + if (!array_key_exists($key, $array2)) { $result[$key] = $value; continue; } if (is_array($value)) { - $recursiveArrayDiff = static::compare($value, $array2[$key]); + $recursiveArrayDiff = static::compare($value, $array2[$key], $strict); - if (count($recursiveArrayDiff)) { + if (count($recursiveArrayDiff) > 0) { $result[$key] = $recursiveArrayDiff; } @@ -34,16 +44,43 @@ public static function compare($array1, $array2) $value1 = $value; $value2 = $array2[$key]; + if (is_float($value1) || is_float($value2)) { - $value1 = (string)$value1; - $value2 = (string)$value2; + $value1 = (string) $value1; + $value2 = (string) $value2; } - if ($value1 != $value2) { + if ($strict ? $value1 !== $value2 : $value1 != $value2) { $result[$key] = $value; } } return $result; } + + /** + * Returns an array with a strict comparison between $array1 and $array2 + * + * @param array $array1 + * @param array $array2 + * + * @return array + */ + public static function strictComparison($array1, $array2) + { + return static::compare($array1, $array2, true); + } + + /** + * Returns an array with a loose comparison between $array1 and $array2 + * + * @param array $array1 + * @param array $array2 + * + * @return array + */ + public static function looseComparison($array1, $array2) + { + return static::compare($array1, $array2, false); + } } diff --git a/tests/ArrayCompareTest.php b/tests/ArrayCompareTest.php index b7d5729..7bfafe0 100644 --- a/tests/ArrayCompareTest.php +++ b/tests/ArrayCompareTest.php @@ -2,80 +2,277 @@ namespace Rogervila\Test; +use PHPUnit\Framework\TestCase; use Rogervila\ArrayDiffMultidimensional; -class ArrayCompareTest extends \PHPUnit_Framework_TestCase +class ArrayCompareTest extends TestCase { - protected $diff; - - public function setUp() - { - $this->diff = new ArrayDiffMultidimensional(); - } - - /** @test */ - public function returnsAnArray() - { - $this->assertTrue( is_array($this->diff->compare([],[])) ); - } - - /** @test */ - public function DetectsTheDifferenceOnStringValue() - { - $old = [ - 'a' => 'b', - 'c' => uniqid(), - ]; - - $new = [ - 'a' => 'b', - 'c' => uniqid(), - ]; - - $this->assertEquals( count($this->diff->compare($new,$old)), 1 ); - $this->assertTrue( isset($this->diff->compare($new,$old)['c']) ); - } - - /** @test */ - public function DetectsChangeFromStringToArray() - { - $new = [ - 'a' => 'b', - 'c' => [ - 'd' => uniqid(), - 'e' => uniqid(), - ], - ]; - - $old = [ - 'a' => 'b', - 'c' => uniqid(), - ]; - - $this->assertEquals( count($this->diff->compare($new,$old)), 1 ); - $this->assertTrue( is_array($this->diff->compare($new,$old)['c']) ); - } - - /** @test */ - public function DetectsChangesOnNestedArrays() - { - $new = [ - 'a' => 'b', - 'c' => [ - 'd' => 'e', - 'f' => uniqid(), - ], - ]; - - $old = [ - 'a' => 'b', - 'c' => [ - 'd' => 'e', - 'f' => uniqid(), - ], - ]; - - $this->assertEquals( count($this->diff->compare($new,$old)), 1 ); - $this->assertTrue( isset($this->diff->compare($new,$old)['c']['f']) ); - } -} \ No newline at end of file + /** @test */ + public function it_returns_an_array() + { + $diff = new ArrayDiffMultidimensional(); + $this->assertTrue(is_array($diff->compare([], []))); + } + + /** @test */ + public function it_fails_if_first_argument_is_not_an_array() + { + if (method_exists($this, 'expectException')) { + $this->expectException(\InvalidArgumentException::class); + $diff = new ArrayDiffMultidimensional(); + + $diff->compare('this should be an array', 'whatever'); + } else { + var_dump('Skipped since current PHPUnit version does not support expectException'); + $this->assertTrue(true); + } + } + + /** @test */ + public function it_does_not_change_if_second_argument_is_not_an_array() + { + $diff = new ArrayDiffMultidimensional(); + + $old = [ + 'a' => 'b', + 'c' => [ + 'd' => 'e', + 'ff' => [ + 'test' + ] + ], + ]; + + $new = 'anything except an array'; + + $this->assertEquals($old, $diff->compare($old, $new)); + } + + /** @test */ + public function it_detects_the_difference_on_string_value() + { + $diff = new ArrayDiffMultidimensional(); + + $old = [ + 'a' => 'b', + 'c' => uniqid(), + ]; + + $new = [ + 'a' => 'b', + 'c' => uniqid(), + ]; + + $this->assertEquals(1, count($diff->compare($new, $old))); + $this->assertTrue(isset($diff->compare($new, $old)['c'])); + $this->assertTrue(is_string($diff->compare($new, $old)['c'])); + $this->assertFalse(isset($diff->compare($new, $old)['a'])); + } + + /** @test */ + public function it_detects_change_from_string_to_array() + { + $diff = new ArrayDiffMultidimensional(); + + $new = [ + 'a' => 'b', + 'c' => [ + 'd' => uniqid(), + 'e' => uniqid(), + ], + ]; + + $old = [ + 'a' => 'b', + 'c' => uniqid(), + ]; + + $this->assertEquals(1, count($diff->compare($new, $old))); + $this->assertTrue(is_array($diff->compare($new, $old)['c'])); + $this->assertFalse(isset($diff->compare($new, $old)['a'])); + } + + /** @test */ + public function it_detects_changes_on_nested_arrays() + { + $diff = new ArrayDiffMultidimensional(); + + $new = [ + 'a' => 'b', + 'c' => [ + 'd' => 'e', + 'f' => uniqid(), + ], + ]; + + $old = [ + 'a' => 'b', + 'c' => [ + 'd' => 'e', + 'f' => uniqid(), + ], + ]; + + $this->assertEquals(1, count($diff->compare($new, $old))); + $this->assertTrue(isset($diff->compare($new, $old)['c']['f'])); + $this->assertFalse(isset($diff->compare($new, $old)['a'])); + } + + /** @test */ + public function it_detects_change_from_float_to_array() + { + $diff = new ArrayDiffMultidimensional(); + $newfloat = defined('PHP_FLOAT_MAX') ? PHP_FLOAT_MAX : 1.0000000000002; + $oldfloat = 1.0000000000004; + + $new = [ + 'a' => 'b', + 'c' => $newfloat, + ]; + + $old = [ + 'a' => 'b', + 'c' => $oldfloat, + ]; + + $this->assertEquals(1, count($diff->compare($new, $old))); + $this->assertEquals($newfloat, $diff->compare($new, $old)['c']); + $this->assertTrue(is_float($diff->compare($new, $old)['c'])); + $this->assertFalse(isset($diff->compare($new, $old)['a'])); + } + + /** @test */ + public function it_detects_floats_do_not_change() + { + $diff = new ArrayDiffMultidimensional(); + $floatval = defined('PHP_FLOAT_MAX') ? PHP_FLOAT_MAX : 1.0000000000005; + + $new = [ + 'a' => 'b', + 'c' => $floatval, + ]; + + $old = [ + 'a' => 'd', + 'c' => $floatval, + ]; + + $this->assertEquals(1, count($diff->compare($new, $old))); + $this->assertEquals('b', $diff->compare($new, $old)['a']); + $this->assertFalse(isset($diff->compare($new, $old)['c'])); + } + + /** @test */ + public function it_works_with_deep_levels() + { + $diff = new ArrayDiffMultidimensional(); + + $old = [ + 'a' => 'b', + 'c' => [ + 'd' => [ + 'e' => [ + 'f' => [ + 'g' => [ + 'h' => 'old' + ] + ] + ] + ] + ], + ]; + + $new = [ + 'a' => 'b', + 'c' => [ + 'd' => [ + 'e' => [ + 'f' => [ + 'g' => [ + 'h' => 'new' + ] + ] + ] + ] + ], + ]; + + $this->assertEquals(1, count($diff->compare($new, $old))); + $this->assertEquals('new', $diff->compare($new, $old)['c']['d']['e']['f']['g']['h']); + $this->assertFalse(isset($diff->compare($new, $old)['a'])); + } + + /** @test */ + public function it_detects_new_array_items() + { + $diff = new ArrayDiffMultidimensional(); + $value = 'this should be detected'; + + $new = [ + 'a' => 'b', + 'c' => 'd', + 'd' => $value, + ]; + + $old = [ + 'a' => 'b', + 'c' => 'd', + ]; + + $this->assertEquals(1, count($diff->compare($new, $old))); + $this->assertTrue(isset($diff->compare($new, $old)['d'])); + $this->assertEquals($value, $diff->compare($new, $old)['d']); + $this->assertFalse(isset($diff->compare($new, $old)['a'])); + $this->assertFalse(isset($diff->compare($new, $old)['c'])); + } + + /** @test */ + public function it_detects_loose_changes_with_strict_mode() + { + $diff = new ArrayDiffMultidimensional(); + + $new = [ + 'a' => 'b', + 'c' => 1714, + ]; + + $old = [ + 'a' => 'b', + 'c' => '1714', + ]; + + $this->assertEquals(1, count($diff->compare($new, $old))); + $this->assertTrue(isset($diff->compare($new, $old)['c'])); + $this->assertEquals(1714, $diff->compare($new, $old)['c']); + + $this->assertEquals(1, count($diff->compare($new, $old, true))); + $this->assertTrue(isset($diff->compare($new, $old, true)['c'])); + $this->assertEquals(1714, $diff->compare($new, $old, true)['c']); + + $this->assertEquals(1, count($diff->strictComparison($new, $old))); + $this->assertTrue(isset($diff->strictComparison($new, $old)['c'])); + $this->assertEquals(1714, $diff->strictComparison($new, $old)['c']); + } + + /** @test */ + public function it_does_not_detect_loose_changes_without_strict_mode() + { + $diff = new ArrayDiffMultidimensional(); + + $new = [ + 'a' => 'b', + 'c' => 1714, + ]; + + $old = [ + 'a' => 'b', + 'c' => '1714', + ]; + + $this->assertEquals(0, count($diff->compare($new, $old, false))); + $this->assertFalse(isset($diff->compare($new, $old, false)['c'])); + + $this->assertEquals(0, count($diff->looseComparison($new, $old))); + $this->assertFalse(isset($diff->looseComparison($new, $old)['c'])); + } +}