Ejegg has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/370091 )
Change subject: Update SmashPig and Omnimail-Silverpop ...................................................................... Update SmashPig and Omnimail-Silverpop Change-Id: If480186992d97fc1ef5748e919a24c0f16d4e662 --- M composer/installed.json M symfony/event-dispatcher/ContainerAwareEventDispatcher.php M symfony/event-dispatcher/Debug/TraceableEventDispatcher.php M symfony/event-dispatcher/EventDispatcher.php M symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php M symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php M symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php M symfony/event-dispatcher/Tests/EventTest.php M symfony/event-dispatcher/Tests/GenericEventTest.php M symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php M symfony/event-dispatcher/composer.json M symfony/event-dispatcher/phpunit.xml.dist M symfony/polyfill-mbstring/Mbstring.php M symfony/polyfill-mbstring/composer.json M symfony/yaml/Escaper.php M symfony/yaml/Exception/ParseException.php M symfony/yaml/Inline.php M symfony/yaml/Parser.php M symfony/yaml/Tests/DumperTest.php M symfony/yaml/Tests/Fixtures/sfMergeKey.yml M symfony/yaml/Tests/InlineTest.php M symfony/yaml/Tests/ParseExceptionTest.php M symfony/yaml/Tests/ParserTest.php M symfony/yaml/Tests/YamlTest.php M symfony/yaml/phpunit.xml.dist M wikimedia/omnimail-silverpop/src/Requests/ExportListRequest.php M wikimedia/omnimail-silverpop/src/Responses/Contact.php M wikimedia/smash-pig/Core/Configuration.php A wikimedia/smash-pig/Core/DataFiles/AuditParser.php M wikimedia/smash-pig/Core/DataStores/QueueWrapper.php M wikimedia/smash-pig/Core/Listeners/ListenerBase.php M wikimedia/smash-pig/PaymentProviders/Adyen/Audit/AdyenAudit.php M wikimedia/smash-pig/PaymentProviders/Adyen/ReferenceData.php R wikimedia/smash-pig/PaymentProviders/Amazon/Audit/AmazonAudit.php M wikimedia/smash-pig/PaymentProviders/Amazon/Audit/RefundReport.php M wikimedia/smash-pig/PaymentProviders/Amazon/Audit/SettlementReport.php M wikimedia/smash-pig/PaymentProviders/Amazon/Tests/phpunit/AuditTest.php M wikimedia/smash-pig/PaymentProviders/AstroPay/Audit/AstroPayAudit.php A wikimedia/smash-pig/PaymentProviders/Ingenico/Audit/IngenicoAudit.php M wikimedia/smash-pig/PaymentProviders/Ingenico/HostedCheckoutProvider.php M wikimedia/smash-pig/PaymentProviders/Ingenico/IngenicoPaymentProvider.php A wikimedia/smash-pig/PaymentProviders/Ingenico/ReferenceData.php A wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/chargeback.xml.gz A wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/donation.xml.gz A wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/hostedPaymentStatus.response A wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/paymentStatus.response A wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/refund.xml.gz A wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/AuditTest.php M wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/HostedCheckoutProviderTest.php A wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/IngenicoPaymentProviderTest.php M wikimedia/smash-pig/PaymentProviders/PayPal/Tests/Data/recurring_payment_profile_created_transformed.json M wikimedia/smash-pig/config/paypal/main.yaml 52 files changed, 1,158 insertions(+), 527 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/wikimedia/fundraising/crm/vendor refs/changes/91/370091/1 diff --git a/composer/installed.json b/composer/installed.json index 4a3c9b4..5ba866c 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -560,68 +560,6 @@ ] }, { - "name": "symfony/event-dispatcher", - "version": "v2.8.16", - "version_normalized": "2.8.16.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "74877977f90fb9c3e46378d5764217c55f32df34" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/74877977f90fb9c3e46378d5764217c55f32df34", - "reference": "74877977f90fb9c3e46378d5764217c55f32df34", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5|~3.0.0", - "symfony/dependency-injection": "~2.6|~3.0.0", - "symfony/expression-language": "~2.6|~3.0.0", - "symfony/stopwatch": "~2.3|~3.0.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "time": "2017-01-02T20:30:24+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fab...@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "https://symfony.com" - }, - { "name": "symfony/polyfill-php55", "version": "v1.3.0", "version_normalized": "1.3.0.0", @@ -674,67 +612,6 @@ "homepage": "https://symfony.com", "keywords": [ "compatibility", - "polyfill", - "portable", - "shim" - ] - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.3.0", - "version_normalized": "1.3.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "time": "2016-11-14T01:06:16+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p...@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", "polyfill", "portable", "shim" @@ -795,57 +672,6 @@ } ], "description": "Symfony HttpFoundation Component", - "homepage": "https://symfony.com" - }, - { - "name": "symfony/yaml", - "version": "v2.8.16", - "version_normalized": "2.8.16.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "dbe61fed9cd4a44c5b1d14e5e7b1a8640cfb2bf2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/dbe61fed9cd4a44c5b1d14e5e7b1a8640cfb2bf2", - "reference": "dbe61fed9cd4a44c5b1d14e5e7b1a8640cfb2bf2", - "shasum": "" - }, - "require": { - "php": ">=5.3.9" - }, - "time": "2017-01-03T13:49:52+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fab...@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", "homepage": "https://symfony.com" }, { @@ -1887,13 +1713,187 @@ "description": "Wikimedia Foundation payment processing library" }, { + "name": "symfony/event-dispatcher", + "version": "v2.8.26", + "version_normalized": "2.8.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "1377400fd641d7d1935981546aaef780ecd5bf6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/1377400fd641d7d1935981546aaef780ecd5bf6d", + "reference": "1377400fd641d7d1935981546aaef780ecd5bf6d", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "^2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.6|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2017-06-02T07:47:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fab...@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.4.0", + "version_normalized": "1.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "f29dca382a6485c3cbe6379f0c61230167681937" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937", + "reference": "f29dca382a6485c3cbe6379f0c61230167681937", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2017-06-09T14:24:12+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p...@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ] + }, + { + "name": "symfony/yaml", + "version": "v2.8.26", + "version_normalized": "2.8.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "4c29dec8d489c4e37cf87ccd7166cd0b0e6a45c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/4c29dec8d489c4e37cf87ccd7166cd0b0e6a45c5", + "reference": "4c29dec8d489c4e37cf87ccd7166cd0b0e6a45c5", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "time": "2017-06-01T20:52:29+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fab...@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com" + }, + { "name": "wikimedia/smash-pig", "version": "dev-master", "version_normalized": "9999999-dev", "source": { "type": "git", "url": "https://gerrit.wikimedia.org/r/wikimedia/fundraising/SmashPig.git", - "reference": "28035b9795b5cb70689a142a08377c2d03574956" + "reference": "c188e2e90c4a753d17ca8e9ecbd58aa943b5fce2" }, "require": { "amzn/login-and-pay-with-amazon-sdk-php": "dev-master", @@ -1911,7 +1911,7 @@ "jakub-onderka/php-parallel-lint": "^0.9", "phpunit/phpunit": "^4.8" }, - "time": "2017-07-17T14:48:07+00:00", + "time": "2017-08-03T18:29:58+00:00", "type": "library", "installation-source": "source", "autoload": { @@ -1949,73 +1949,13 @@ ] }, { - "name": "symfony/polyfill-php54", - "version": "v1.3.0", - "version_normalized": "1.3.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php54.git", - "reference": "90e085822963fdcc9d1c5b73deb3d2e5783b16a0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/90e085822963fdcc9d1c5b73deb3d2e5783b16a0", - "reference": "90e085822963fdcc9d1c5b73deb3d2e5783b16a0", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "time": "2016-11-14T01:06:16+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php54\\": "" - }, - "files": [ - "bootstrap.php" - ], - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p...@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ] - }, - { "name": "wikimedia/omnimail-silverpop", "version": "dev-master", "version_normalized": "9999999-dev", "source": { "type": "git", "url": "https://github.com/eileenmcnaughton/omnimail-silverpop.git", - "reference": "cd3933b8ee7b263d0472cc44d047b82f3ec4029c" + "reference": "c3248b3932e62cd2812aa3502d875b7c12c25c66" }, "require": { "league/csv": "^8.0", @@ -2026,7 +1966,7 @@ "require-dev": { "guzzlehttp/guzzle": "*" }, - "time": "2017-07-09T22:55:21+00:00", + "time": "2017-07-27T02:53:46+00:00", "type": "library", "installation-source": "source", "autoload": { diff --git a/symfony/event-dispatcher/ContainerAwareEventDispatcher.php b/symfony/event-dispatcher/ContainerAwareEventDispatcher.php index 6a02e9f..f2d5a40 100644 --- a/symfony/event-dispatcher/ContainerAwareEventDispatcher.php +++ b/symfony/event-dispatcher/ContainerAwareEventDispatcher.php @@ -105,7 +105,7 @@ public function hasListeners($eventName = null) { if (null === $eventName) { - return (bool) count($this->listenerIds) || (bool) count($this->listeners); + return $this->listenerIds || $this->listeners || parent::hasListeners(); } if (isset($this->listenerIds[$eventName])) { diff --git a/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php index 9b460f5..2f6d9be 100644 --- a/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php +++ b/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php @@ -306,6 +306,12 @@ 'event' => $eventName, 'priority' => $this->getListenerPriority($eventName, $listener), ); + + // unwrap for correct listener info + if ($listener instanceof WrappedListener) { + $listener = $listener->getWrappedListener(); + } + if ($listener instanceof \Closure) { $info += array( 'type' => 'Closure', diff --git a/symfony/event-dispatcher/EventDispatcher.php b/symfony/event-dispatcher/EventDispatcher.php index bce44a1..dec7c85 100644 --- a/symfony/event-dispatcher/EventDispatcher.php +++ b/symfony/event-dispatcher/EventDispatcher.php @@ -103,7 +103,7 @@ */ public function hasListeners($eventName = null) { - return (bool) count($this->getListeners($eventName)); + return (bool) $this->getListeners($eventName); } /** diff --git a/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php b/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php index 5e16532..2384916 100644 --- a/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php +++ b/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php @@ -11,11 +11,12 @@ namespace Symfony\Component\EventDispatcher\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase +abstract class AbstractEventDispatcherTest extends TestCase { /* Some pseudo events */ const preFoo = 'pre.foo'; @@ -55,6 +56,7 @@ { $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners()); $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); diff --git a/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php index 46eece7..d99b0bf 100644 --- a/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php +++ b/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -11,14 +11,16 @@ namespace Symfony\Component\EventDispatcher\Tests\Debug; +use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\Debug\WrappedListener; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\Stopwatch\Stopwatch; -class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase +class TraceableEventDispatcherTest extends TestCase { public function testAddRemoveListener() { @@ -99,19 +101,39 @@ $this->assertCount(0, $dispatcher->getListeners('foo')); } - public function testGetCalledListeners() + /** + * @dataProvider isWrappedDataProvider + * + * @param bool $isWrapped + */ + public function testGetCalledListeners($isWrapped) { $dispatcher = new EventDispatcher(); - $tdispatcher = new TraceableEventDispatcher($dispatcher, new Stopwatch()); - $tdispatcher->addListener('foo', $listener = function () {}); + $stopWatch = new Stopwatch(); + $tdispatcher = new TraceableEventDispatcher($dispatcher, $stopWatch); + + $listener = function () {}; + if ($isWrapped) { + $listener = new WrappedListener($listener, 'foo', $stopWatch, $dispatcher); + } + + $tdispatcher->addListener('foo', $listener, 5); $this->assertEquals(array(), $tdispatcher->getCalledListeners()); - $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 0)), $tdispatcher->getNotCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 5)), $tdispatcher->getNotCalledListeners()); $tdispatcher->dispatch('foo'); - $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => null)), $tdispatcher->getCalledListeners()); + $this->assertEquals(array('foo.closure' => array('event' => 'foo', 'type' => 'Closure', 'pretty' => 'closure', 'priority' => 5)), $tdispatcher->getCalledListeners()); $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); + } + + public function isWrappedDataProvider() + { + return array( + array(false), + array(true), + ); } public function testGetCalledListenersNested() @@ -177,14 +199,20 @@ { $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); $loop = 1; + $dispatchedEvents = 0; $dispatcher->addListener('foo', $listener1 = function () use ($dispatcher, &$loop) { ++$loop; if (2 == $loop) { $dispatcher->dispatch('foo'); } }); + $dispatcher->addListener('foo', function () use (&$dispatchedEvents) { + ++$dispatchedEvents; + }); $dispatcher->dispatch('foo'); + + $this->assertSame(2, $dispatchedEvents); } public function testDispatchReusedEventNested() diff --git a/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php index cb04f74..53d7282 100644 --- a/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php +++ b/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php @@ -11,10 +11,11 @@ namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection; +use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; -class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase +class RegisterListenersPassTest extends TestCase { /** * Tests that event subscribers not implementing EventSubscriberInterface diff --git a/symfony/event-dispatcher/Tests/EventTest.php b/symfony/event-dispatcher/Tests/EventTest.php index 9a82267..bdc14ab 100644 --- a/symfony/event-dispatcher/Tests/EventTest.php +++ b/symfony/event-dispatcher/Tests/EventTest.php @@ -11,13 +11,14 @@ namespace Symfony\Component\EventDispatcher\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventDispatcher; /** * Test class for Event. */ -class EventTest extends \PHPUnit_Framework_TestCase +class EventTest extends TestCase { /** * @var \Symfony\Component\EventDispatcher\Event diff --git a/symfony/event-dispatcher/Tests/GenericEventTest.php b/symfony/event-dispatcher/Tests/GenericEventTest.php index aebd82d..c84d3ac 100644 --- a/symfony/event-dispatcher/Tests/GenericEventTest.php +++ b/symfony/event-dispatcher/Tests/GenericEventTest.php @@ -11,12 +11,13 @@ namespace Symfony\Component\EventDispatcher\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\GenericEvent; /** * Test class for Event. */ -class GenericEventTest extends \PHPUnit_Framework_TestCase +class GenericEventTest extends TestCase { /** * @var GenericEvent @@ -95,7 +96,7 @@ $this->assertEquals('Event', $this->event['name']); // test getting invalid arg - $this->setExpectedException('InvalidArgumentException'); + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('InvalidArgumentException'); $this->assertFalse($this->event['nameNotExist']); } diff --git a/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php b/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php index 0f88680..04f2861 100644 --- a/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php +++ b/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php @@ -11,13 +11,14 @@ namespace Symfony\Component\EventDispatcher\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; /** * @author Bernhard Schussek <bschus...@gmail.com> */ -class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase +class ImmutableEventDispatcherTest extends TestCase { /** * @var \PHPUnit_Framework_MockObject_MockObject diff --git a/symfony/event-dispatcher/composer.json b/symfony/event-dispatcher/composer.json index 282b770..14fc24b 100644 --- a/symfony/event-dispatcher/composer.json +++ b/symfony/event-dispatcher/composer.json @@ -21,7 +21,7 @@ "require-dev": { "symfony/dependency-injection": "~2.6|~3.0.0", "symfony/expression-language": "~2.6|~3.0.0", - "symfony/config": "~2.0,>=2.0.5|~3.0.0", + "symfony/config": "^2.0.5|~3.0.0", "symfony/stopwatch": "~2.3|~3.0.0", "psr/log": "~1.0" }, diff --git a/symfony/event-dispatcher/phpunit.xml.dist b/symfony/event-dispatcher/phpunit.xml.dist index ae0586e..b3ad1bd 100644 --- a/symfony/event-dispatcher/phpunit.xml.dist +++ b/symfony/event-dispatcher/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > <php> <ini name="error_reporting" value="-1" /> diff --git a/symfony/polyfill-mbstring/Mbstring.php b/symfony/polyfill-mbstring/Mbstring.php index 934cfcf..97e8c9b 100644 --- a/symfony/polyfill-mbstring/Mbstring.php +++ b/symfony/polyfill-mbstring/Mbstring.php @@ -147,6 +147,9 @@ if ('UTF-8' === $encoding) { $encoding = null; + if (!preg_match('//u', $s)) { + $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); + } } else { $s = iconv($encoding, 'UTF-8//IGNORE', $s); } @@ -336,10 +339,9 @@ public static function mb_strlen($s, $encoding = null) { - switch ($encoding = self::getEncoding($encoding)) { - case 'ASCII': - case 'CP850': - return strlen($s); + $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strlen($s); } return @iconv_strlen($s, $encoding); @@ -348,6 +350,9 @@ public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strpos($haystack, $needle, $offset); + } if ('' === $needle .= '') { trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); @@ -361,6 +366,9 @@ public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrpos($haystack, $needle, $offset); + } if ($offset != (int) $offset) { $offset = 0; @@ -400,6 +408,9 @@ public static function mb_substr($s, $start, $length = null, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return substr($s, $start, null === $length ? 2147483647 : $length); + } if ($start < 0) { $start = iconv_strlen($s, $encoding) + $start; @@ -438,6 +449,9 @@ public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) { $encoding = self::getEncoding($encoding); + if ('CP850' === $encoding || 'ASCII' === $encoding) { + return strrchr($haystack, $needle, $part); + } $needle = self::mb_substr($needle, 0, 1, $encoding); $pos = iconv_strrpos($haystack, $needle, $encoding); diff --git a/symfony/polyfill-mbstring/composer.json b/symfony/polyfill-mbstring/composer.json index 24eefbd..48fc3dd 100644 --- a/symfony/polyfill-mbstring/composer.json +++ b/symfony/polyfill-mbstring/composer.json @@ -28,7 +28,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.4-dev" } } } diff --git a/symfony/yaml/Escaper.php b/symfony/yaml/Escaper.php index a74f14d..94bb392 100644 --- a/symfony/yaml/Escaper.php +++ b/symfony/yaml/Escaper.php @@ -33,13 +33,15 @@ "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", - "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9"); + "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9", + ); private static $escaped = array('\\\\', '\\"', '\\\\', '\\"', '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', - '\\N', '\\_', '\\L', '\\P'); + '\\N', '\\_', '\\L', '\\P', + ); /** * Determines if a PHP value would require double quoting in YAML. @@ -50,7 +52,7 @@ */ public static function requiresDoubleQuoting($value) { - return preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); + return 0 < preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); } /** @@ -82,7 +84,7 @@ // Determines if the PHP value contains any single characters that would // cause it to require single quoting in YAML. - return preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value); + return 0 < preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value); } /** diff --git a/symfony/yaml/Exception/ParseException.php b/symfony/yaml/Exception/ParseException.php index b74eb91..ef36cfb 100644 --- a/symfony/yaml/Exception/ParseException.php +++ b/symfony/yaml/Exception/ParseException.php @@ -26,11 +26,11 @@ /** * Constructor. * - * @param string $message The error message - * @param int $parsedLine The line where the error occurred - * @param int $snippet The snippet of code near the problem - * @param string $parsedFile The file name where the error occurred - * @param \Exception $previous The previous exception + * @param string $message The error message + * @param int $parsedLine The line where the error occurred + * @param string|null $snippet The snippet of code near the problem + * @param string|null $parsedFile The file name where the error occurred + * @param \Exception|null $previous The previous exception */ public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null) { @@ -123,7 +123,7 @@ } if (null !== $this->parsedFile) { - if (PHP_VERSION_ID >= 50400) { + if (\PHP_VERSION_ID >= 50400) { $jsonOptions = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; } else { $jsonOptions = 0; diff --git a/symfony/yaml/Inline.php b/symfony/yaml/Inline.php index 74d23be..dade755 100644 --- a/symfony/yaml/Inline.php +++ b/symfony/yaml/Inline.php @@ -21,7 +21,7 @@ */ class Inline { - const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; + const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')'; private static $exceptionOnInvalidType = false; private static $objectSupport = false; @@ -149,8 +149,8 @@ case Escaper::requiresDoubleQuoting($value): return Escaper::escapeWithDoubleQuotes($value); case Escaper::requiresSingleQuoting($value): - case preg_match(self::getHexRegex(), $value): - case preg_match(self::getTimestampRegex(), $value): + case Parser::preg_match(self::getHexRegex(), $value): + case Parser::preg_match(self::getTimestampRegex(), $value): return Escaper::escapeWithSingleQuotes($value); default: return $value; @@ -212,12 +212,12 @@ /** * Parses a YAML scalar. * - * @param string $scalar - * @param string $delimiters - * @param array $stringDelimiters - * @param int &$i - * @param bool $evaluate - * @param array $references + * @param string $scalar + * @param string[] $delimiters + * @param string[] $stringDelimiters + * @param int &$i + * @param bool $evaluate + * @param array $references * * @return string * @@ -244,10 +244,10 @@ $i += strlen($output); // remove comments - if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { + if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) { $output = substr($output, 0, $match[0][1]); } - } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { + } elseif (Parser::preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { $output = $match[1]; $i += strlen($output); } else { @@ -282,7 +282,7 @@ */ private static function parseQuotedScalar($scalar, &$i) { - if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { + if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i))); } @@ -453,7 +453,7 @@ * @param string $scalar * @param array $references * - * @return string A YAML string + * @return mixed The evaluated YAML string * * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved */ @@ -530,16 +530,16 @@ return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw); case is_numeric($scalar): - case preg_match(self::getHexRegex(), $scalar): + case Parser::preg_match(self::getHexRegex(), $scalar): return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar; case '.inf' === $scalarLower: case '.nan' === $scalarLower: return -log(0); case '-.inf' === $scalarLower: return log(0); - case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): + case Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): return (float) str_replace(',', '', $scalar); - case preg_match(self::getTimestampRegex(), $scalar): + case Parser::preg_match(self::getTimestampRegex(), $scalar): $timeZone = date_default_timezone_get(); date_default_timezone_set('UTC'); $time = strtotime($scalar); diff --git a/symfony/yaml/Parser.php b/symfony/yaml/Parser.php index 97db3af..96a85a8 100644 --- a/symfony/yaml/Parser.php +++ b/symfony/yaml/Parser.php @@ -61,26 +61,60 @@ */ public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) { - if (!preg_match('//u', $value)) { + if (false === preg_match('//u', $value)) { throw new ParseException('The YAML value does not appear to be valid UTF-8.'); } - $this->currentLineNb = -1; - $this->currentLine = ''; - $value = $this->cleanup($value); - $this->lines = explode("\n", $value); - if (null === $this->totalNumberOfLines) { - $this->totalNumberOfLines = count($this->lines); - } + $this->refs = array(); + + $mbEncoding = null; + $e = null; + $data = null; if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) { $mbEncoding = mb_internal_encoding(); mb_internal_encoding('UTF-8'); } + try { + $data = $this->doParse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap); + } catch (\Exception $e) { + } catch (\Throwable $e) { + } + + if (null !== $mbEncoding) { + mb_internal_encoding($mbEncoding); + } + + $this->lines = array(); + $this->currentLine = ''; + $this->refs = array(); + $this->skippedLineNumbers = array(); + $this->locallySkippedLineNumbers = array(); + + if (null !== $e) { + throw $e; + } + + return $data; + } + + private function doParse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false) + { + $this->currentLineNb = -1; + $this->currentLine = ''; + $value = $this->cleanup($value); + $this->lines = explode("\n", $value); + $this->locallySkippedLineNumbers = array(); + + if (null === $this->totalNumberOfLines) { + $this->totalNumberOfLines = count($this->lines); + } + $data = array(); $context = null; $allowOverwrite = false; + while ($this->moveToNextLine()) { if ($this->isCurrentLineEmpty()) { continue; @@ -92,13 +126,13 @@ } $isRef = $mergeNode = false; - if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) { + if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) { if ($context && 'mapping' == $context) { throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine); } $context = 'sequence'; - if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { + if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $values['value'] = $matches['value']; } @@ -108,7 +142,7 @@ $data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap); } else { if (isset($values['leadspaces']) - && preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches) + && self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($values['value']), $matches) ) { // this is a compact notation element, add to next block and parse $block = $values['value']; @@ -124,7 +158,10 @@ if ($isRef) { $this->refs[$isRef] = end($data); } - } elseif (preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) { + } elseif ( + self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($this->currentLine), $values) + && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'"))) + ) { if ($context && 'sequence' == $context) { throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine); } @@ -161,11 +198,7 @@ throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } - foreach ($refValue as $key => $value) { - if (!isset($data[$key])) { - $data[$key] = $value; - } - } + $data += $refValue; // array union } else { if (isset($values['value']) && $values['value'] !== '') { $value = $values['value']; @@ -187,23 +220,15 @@ throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); } - foreach ($parsedItem as $key => $value) { - if (!isset($data[$key])) { - $data[$key] = $value; - } - } + $data += $parsedItem; // array union } } else { // If the value associated with the key is a single mapping node, each of its key/value pairs is inserted into the // current mapping, unless the key already exists in it. - foreach ($parsed as $key => $value) { - if (!isset($data[$key])) { - $data[$key] = $value; - } - } + $data += $parsed; // array union } } - } elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { + } elseif (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) { $isRef = $matches['ref']; $values['value'] = $matches['value']; } @@ -255,39 +280,11 @@ throw $e; } - if (isset($mbEncoding)) { - mb_internal_encoding($mbEncoding); - } - return $value; } - switch (preg_last_error()) { - case PREG_INTERNAL_ERROR: - $error = 'Internal PCRE error.'; - break; - case PREG_BACKTRACK_LIMIT_ERROR: - $error = 'pcre.backtrack_limit reached.'; - break; - case PREG_RECURSION_LIMIT_ERROR: - $error = 'pcre.recursion_limit reached.'; - break; - case PREG_BAD_UTF8_ERROR: - $error = 'Malformed UTF-8 data.'; - break; - case PREG_BAD_UTF8_OFFSET_ERROR: - $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; - break; - default: - $error = 'Unable to parse.'; - } - - throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine); + throw new ParseException('Unable to parse.', $this->getRealCurrentLineNb() + 1, $this->currentLine); } - } - - if (isset($mbEncoding)) { - mb_internal_encoding($mbEncoding); } if ($objectForMap && !is_object($data) && 'mapping' === $context) { @@ -318,7 +315,7 @@ $parser = new self($offset, $this->totalNumberOfLines, $skippedLineNumbers); $parser->refs = &$this->refs; - return $parser->parse($yaml, $exceptionOnInvalidType, $objectSupport, $objectForMap); + return $parser->doParse($yaml, $exceptionOnInvalidType, $objectSupport, $objectForMap); } /** @@ -527,7 +524,7 @@ return $this->refs[$value]; } - if (preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { + if (self::preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) { $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; return $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers)); @@ -582,7 +579,7 @@ // determine indentation if not specified if (0 === $indentation) { - if (preg_match('/^ +/', $this->currentLine, $matches)) { + if (self::preg_match('/^ +/', $this->currentLine, $matches)) { $indentation = strlen($matches[0]); } } @@ -593,7 +590,7 @@ while ( $notEOF && ( $isCurrentLineBlank || - preg_match($pattern, $this->currentLine, $matches) + self::preg_match($pattern, $this->currentLine, $matches) ) ) { if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) { @@ -626,7 +623,7 @@ $previousLineIndented = false; $previousLineBlank = false; - for ($i = 0; $i < count($blockLines); ++$i) { + for ($i = 0, $blockLinesCount = count($blockLines); $i < $blockLinesCount; ++$i) { if ('' === $blockLines[$i]) { $text .= "\n"; $previousLineIndented = false; @@ -681,10 +678,7 @@ return false; } - $ret = false; - if ($this->getCurrentLineIndentation() > $currentIndentation) { - $ret = true; - } + $ret = $this->getCurrentLineIndentation() > $currentIndentation; $this->moveToPreviousLine(); @@ -785,14 +779,7 @@ return false; } - $ret = false; - if ( - $this->getCurrentLineIndentation() == $currentIndentation - && - $this->isStringUnIndentedCollectionItem() - ) { - $ret = true; - } + $ret = $this->getCurrentLineIndentation() === $currentIndentation && $this->isStringUnIndentedCollectionItem(); $this->moveToPreviousLine(); @@ -816,6 +803,48 @@ */ private function isBlockScalarHeader() { - return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); + return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine); + } + + /** + * A local wrapper for `preg_match` which will throw a ParseException if there + * is an internal error in the PCRE engine. + * + * This avoids us needing to check for "false" every time PCRE is used + * in the YAML engine + * + * @throws ParseException on a PCRE internal error + * + * @see preg_last_error() + * + * @internal + */ + public static function preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0) + { + if (false === $ret = preg_match($pattern, $subject, $matches, $flags, $offset)) { + switch (preg_last_error()) { + case PREG_INTERNAL_ERROR: + $error = 'Internal PCRE error.'; + break; + case PREG_BACKTRACK_LIMIT_ERROR: + $error = 'pcre.backtrack_limit reached.'; + break; + case PREG_RECURSION_LIMIT_ERROR: + $error = 'pcre.recursion_limit reached.'; + break; + case PREG_BAD_UTF8_ERROR: + $error = 'Malformed UTF-8 data.'; + break; + case PREG_BAD_UTF8_OFFSET_ERROR: + $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; + break; + default: + $error = 'Error.'; + } + + throw new ParseException($error); + } + + return $ret; } } diff --git a/symfony/yaml/Tests/DumperTest.php b/symfony/yaml/Tests/DumperTest.php index 84069d8..6a1b3ac 100644 --- a/symfony/yaml/Tests/DumperTest.php +++ b/symfony/yaml/Tests/DumperTest.php @@ -11,10 +11,11 @@ namespace Symfony\Component\Yaml\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Dumper; -class DumperTest extends \PHPUnit_Framework_TestCase +class DumperTest extends TestCase { protected $parser; protected $dumper; @@ -209,23 +210,25 @@ public function getEscapeSequences() { return array( - 'null' => array("\t\\0", '"\t\\\\0"'), - 'bell' => array("\t\\a", '"\t\\\\a"'), - 'backspace' => array("\t\\b", '"\t\\\\b"'), - 'horizontal-tab' => array("\t\\t", '"\t\\\\t"'), - 'line-feed' => array("\t\\n", '"\t\\\\n"'), - 'vertical-tab' => array("\t\\v", '"\t\\\\v"'), - 'form-feed' => array("\t\\f", '"\t\\\\f"'), - 'carriage-return' => array("\t\\r", '"\t\\\\r"'), - 'escape' => array("\t\\e", '"\t\\\\e"'), - 'space' => array("\t\\ ", '"\t\\\\ "'), - 'double-quote' => array("\t\\\"", '"\t\\\\\\""'), - 'slash' => array("\t\\/", '"\t\\\\/"'), - 'backslash' => array("\t\\\\", '"\t\\\\\\\\"'), - 'next-line' => array("\t\\N", '"\t\\\\N"'), - 'non-breaking-space' => array("\t\\�", '"\t\\\\�"'), - 'line-separator' => array("\t\\L", '"\t\\\\L"'), - 'paragraph-separator' => array("\t\\P", '"\t\\\\P"'), + 'empty string' => array('', "''"), + 'null' => array("\x0", '"\\0"'), + 'bell' => array("\x7", '"\\a"'), + 'backspace' => array("\x8", '"\\b"'), + 'horizontal-tab' => array("\t", '"\\t"'), + 'line-feed' => array("\n", '"\\n"'), + 'vertical-tab' => array("\v", '"\\v"'), + 'form-feed' => array("\xC", '"\\f"'), + 'carriage-return' => array("\r", '"\\r"'), + 'escape' => array("\x1B", '"\\e"'), + 'space' => array(' ', "' '"), + 'double-quote' => array('"', "'\"'"), + 'slash' => array('/', '/'), + 'backslash' => array('\\', '\\'), + 'next-line' => array("\xC2\x85", '"\\N"'), + 'non-breaking-space' => array("\xc2\xa0", '"\\_"'), + 'line-separator' => array("\xE2\x80\xA8", '"\\L"'), + 'paragraph-separator' => array("\xE2\x80\xA9", '"\\P"'), + 'colon' => array(':', "':'"), ); } diff --git a/symfony/yaml/Tests/Fixtures/sfMergeKey.yml b/symfony/yaml/Tests/Fixtures/sfMergeKey.yml index 4b67d34..59f6125 100644 --- a/symfony/yaml/Tests/Fixtures/sfMergeKey.yml +++ b/symfony/yaml/Tests/Fixtures/sfMergeKey.yml @@ -10,9 +10,11 @@ a: Steve b: Clark c: Brian + e: notnull bar: a: before d: other + e: ~ <<: *foo b: new x: Oren @@ -46,13 +48,13 @@ <<: *nestedref php: | array( - 'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian'), - 'bar' => array('a' => 'before', 'd' => 'other', 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), + 'foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull'), + 'bar' => array('a' => 'before', 'd' => 'other', 'e' => null, 'b' => 'new', 'c' => array('foo' => 'bar', 'bar' => 'foo'), 'x' => 'Oren'), 'duplicate' => array('foo' => 'bar'), 'foo2' => array('a' => 'Ballmer'), 'ding' => array('fi', 'fei', 'fo', 'fam'), - 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), - 'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam'), + 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), + 'head' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'e' => 'notnull', 'fi', 'fei', 'fo', 'fam'), 'taz' => array('a' => 'Steve', 'w' => array('p' => 1234)), 'nested' => array('a' => 'Steve', 'w' => array('p' => 12345), 'd' => 'Doug', 'z' => array('p' => 12345)) ) diff --git a/symfony/yaml/Tests/InlineTest.php b/symfony/yaml/Tests/InlineTest.php index bc7921f..7ca692d 100644 --- a/symfony/yaml/Tests/InlineTest.php +++ b/symfony/yaml/Tests/InlineTest.php @@ -11,9 +11,10 @@ namespace Symfony\Component\Yaml\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Component\Yaml\Inline; -class InlineTest extends \PHPUnit_Framework_TestCase +class InlineTest extends TestCase { /** * @dataProvider getTestsForParse @@ -192,27 +193,42 @@ /** * @group legacy - * @dataProvider getReservedIndicators + * @expectedDeprecation Not quoting the scalar "@foo " starting with "@" is deprecated since Symfony 2.8 and will throw a ParseException in 3.0. * throws \Symfony\Component\Yaml\Exception\ParseException in 3.0 */ - public function testParseUnquotedScalarStartingWithReservedIndicator($indicator) + public function testParseUnquotedScalarStartingWithReservedAtIndicator() { - Inline::parse(sprintf('{ foo: %sfoo }', $indicator)); - } - - public function getReservedIndicators() - { - return array(array('@'), array('`')); + Inline::parse('{ foo: @foo }'); } /** * @group legacy - * @dataProvider getScalarIndicators + * @expectedDeprecation Not quoting the scalar "`foo " starting with "`" is deprecated since Symfony 2.8 and will throw a ParseException in 3.0. * throws \Symfony\Component\Yaml\Exception\ParseException in 3.0 */ - public function testParseUnquotedScalarStartingWithScalarIndicator($indicator) + public function testParseUnquotedScalarStartingWithReservedBacktickIndicator() { - Inline::parse(sprintf('{ foo: %sfoo }', $indicator)); + Inline::parse('{ foo: `foo }'); + } + + /** + * @group legacy + * @expectedDeprecation Not quoting the scalar "|foo " starting with "|" is deprecated since Symfony 2.8 and will throw a ParseException in 3.0. + * throws \Symfony\Component\Yaml\Exception\ParseException in 3.0 + */ + public function testParseUnquotedScalarStartingWithLiteralStyleIndicator() + { + Inline::parse('{ foo: |foo }'); + } + + /** + * @group legacy + * @expectedDeprecation Not quoting the scalar ">foo " starting with ">" is deprecated since Symfony 2.8 and will throw a ParseException in 3.0. + * throws \Symfony\Component\Yaml\Exception\ParseException in 3.0 + */ + public function testParseUnquotedScalarStartingWithFoldedStyleIndicator() + { + Inline::parse('{ foo: >foo }'); } public function getScalarIndicators() @@ -457,4 +473,25 @@ { Inline::parse('{this, is not, supported}'); } + + public function testVeryLongQuotedStrings() + { + $longStringWithQuotes = str_repeat("x\r\n\\\"x\"x", 1000); + + $yamlString = Inline::dump(array('longStringWithQuotes' => $longStringWithQuotes)); + $arrayFromYaml = Inline::parse($yamlString); + + $this->assertEquals($longStringWithQuotes, $arrayFromYaml['longStringWithQuotes']); + } + + public function testBooleanMappingKeysAreConvertedToStrings() + { + $this->assertSame(array('false' => 'foo'), Inline::parse('{false: foo}')); + $this->assertSame(array('true' => 'foo'), Inline::parse('{true: foo}')); + } + + public function testTheEmptyStringIsAValidMappingKey() + { + $this->assertSame(array('' => 'foo'), Inline::parse('{ "": foo }')); + } } diff --git a/symfony/yaml/Tests/ParseExceptionTest.php b/symfony/yaml/Tests/ParseExceptionTest.php index e4eb9c9..b7797fb 100644 --- a/symfony/yaml/Tests/ParseExceptionTest.php +++ b/symfony/yaml/Tests/ParseExceptionTest.php @@ -11,14 +11,15 @@ namespace Symfony\Component\Yaml\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Component\Yaml\Exception\ParseException; -class ParseExceptionTest extends \PHPUnit_Framework_TestCase +class ParseExceptionTest extends TestCase { public function testGetMessage() { $exception = new ParseException('Error message', 42, 'foo: bar', '/var/www/app/config.yml'); - if (PHP_VERSION_ID >= 50400) { + if (\PHP_VERSION_ID >= 50400) { $message = 'Error message in "/var/www/app/config.yml" at line 42 (near "foo: bar")'; } else { $message = 'Error message in "\\/var\\/www\\/app\\/config.yml" at line 42 (near "foo: bar")'; @@ -30,7 +31,7 @@ public function testGetMessageWithUnicodeInFilename() { $exception = new ParseException('Error message', 42, 'foo: bar', 'äöü.yml'); - if (PHP_VERSION_ID >= 50400) { + if (\PHP_VERSION_ID >= 50400) { $message = 'Error message in "äöü.yml" at line 42 (near "foo: bar")'; } else { $message = 'Error message in "\u00e4\u00f6\u00fc.yml" at line 42 (near "foo: bar")'; diff --git a/symfony/yaml/Tests/ParserTest.php b/symfony/yaml/Tests/ParserTest.php index 6327fd0..2c3a6e0 100644 --- a/symfony/yaml/Tests/ParserTest.php +++ b/symfony/yaml/Tests/ParserTest.php @@ -11,11 +11,13 @@ namespace Symfony\Component\Yaml\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Parser; -class ParserTest extends \PHPUnit_Framework_TestCase +class ParserTest extends TestCase { + /** @var Parser */ protected $parser; protected function setUp() @@ -1156,10 +1158,12 @@ */ public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml) { - $this->setExpectedException( - '\Symfony\Component\Yaml\Exception\ParseException', - sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber) - ); + if (method_exists($this, 'expectException')) { + $this->expectException('\Symfony\Component\Yaml\Exception\ParseException'); + $this->expectExceptionMessage(sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber)); + } else { + $this->setExpectedException('\Symfony\Component\Yaml\Exception\ParseException', sprintf('Unexpected characters near "," at line %d (near "bar: "123",").', $lineNumber)); + } $this->parser->parse($yaml); } @@ -1216,6 +1220,38 @@ ), ); } + + public function testCanParseVeryLongValue() + { + $longStringWithSpaces = str_repeat('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ', 20000); + $trickyVal = array('x' => $longStringWithSpaces); + + $yamlString = Yaml::dump($trickyVal); + $arrayFromYaml = $this->parser->parse($yamlString); + + $this->assertEquals($trickyVal, $arrayFromYaml); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * @expectedExceptionMessage Reference "foo" does not exist at line 2 + */ + public function testParserCleansUpReferencesBetweenRuns() + { + $yaml = <<<YAML +foo: &foo + baz: foobar +bar: + <<: *foo +YAML; + $this->parser->parse($yaml); + + $yaml = <<<YAML +bar: + <<: *foo +YAML; + $this->parser->parse($yaml); + } } class B diff --git a/symfony/yaml/Tests/YamlTest.php b/symfony/yaml/Tests/YamlTest.php index 883ee83..9e776ca 100644 --- a/symfony/yaml/Tests/YamlTest.php +++ b/symfony/yaml/Tests/YamlTest.php @@ -11,9 +11,10 @@ namespace Symfony\Component\Yaml\Tests; +use PHPUnit\Framework\TestCase; use Symfony\Component\Yaml\Yaml; -class YamlTest extends \PHPUnit_Framework_TestCase +class YamlTest extends TestCase { public function testParseAndDump() { diff --git a/symfony/yaml/phpunit.xml.dist b/symfony/yaml/phpunit.xml.dist index 6bdbea1..7c732f8 100644 --- a/symfony/yaml/phpunit.xml.dist +++ b/symfony/yaml/phpunit.xml.dist @@ -5,6 +5,8 @@ backupGlobals="false" colors="true" bootstrap="vendor/autoload.php" + failOnRisky="true" + failOnWarning="true" > <php> <ini name="error_reporting" value="-1" /> diff --git a/wikimedia/omnimail-silverpop/src/Requests/ExportListRequest.php b/wikimedia/omnimail-silverpop/src/Requests/ExportListRequest.php index 9bb7d56..ffb87fb 100644 --- a/wikimedia/omnimail-silverpop/src/Requests/ExportListRequest.php +++ b/wikimedia/omnimail-silverpop/src/Requests/ExportListRequest.php @@ -23,6 +23,31 @@ protected $groupIdentifier; /** + * Specifies which contacts to export. Valid values are: + * - ALL – export entire database. System columns will not be exported by default. + * - OPT_IN – export only currently opted-in contacts. + * - OPT_OUT – export only currently opted-out contacts. + * - UNDELIVERABLE – export only contacts who are currently marked as undeliverable. + * + * @var string + */ + protected $exportType; + + /** + * @return string + */ + public function getExportType() { + return $this->exportType; + } + + /** + * @param string $exportType + */ + public function setExportType($exportType) { + $this->exportType = $exportType; + } + + /** * @return mixed */ public function getGroupIdentifier() { @@ -108,7 +133,8 @@ $result = $this->silverPop->exportList( $this->getGroupIdentifier(), $this->getStartTimeStamp(), - $this->getEndTimeStamp() + $this->getEndTimeStamp(), + $this->getExportType() ); $this->setRetrievalParameters(array( 'jobId' => (string) $result['jobId'], @@ -129,6 +155,7 @@ 'includeTest' => FALSE, 'startTimeStamp' => strtotime('1 week ago'), 'endTimeStamp' => strtotime('now'), + 'exportType' => 'ALL' ); } diff --git a/wikimedia/omnimail-silverpop/src/Responses/Contact.php b/wikimedia/omnimail-silverpop/src/Responses/Contact.php index b2ae90a..75dab07 100644 --- a/wikimedia/omnimail-silverpop/src/Responses/Contact.php +++ b/wikimedia/omnimail-silverpop/src/Responses/Contact.php @@ -75,7 +75,7 @@ * @return false|string */ public function getOptOutIsoDateTime() { - return (date('Y-m-d H:i:s', $this->getOptOutTimestamp())); + return (empty($this->getOptOutTimestamp()) ? FALSE : date('Y-m-d H:i:s', $this->getOptOutTimestamp())); } /** diff --git a/wikimedia/smash-pig/Core/Configuration.php b/wikimedia/smash-pig/Core/Configuration.php index 3923c7a..bf59f2a 100644 --- a/wikimedia/smash-pig/Core/Configuration.php +++ b/wikimedia/smash-pig/Core/Configuration.php @@ -104,15 +104,12 @@ * Obtain a value from the configuration. If the key does not exist this will throw an * exception. * - * @param string $node Parameter node to obtain. If this contains '/' it is assumed that the + * @param string $path Parameter node to obtain. If this contains '/' it is assumed that the * value is contained under additional keys. - * @param bool $returnRef If true will return a reference to the configuration node. This will - * mean that any modifications to the node will be stored in RAM for the - * duration of the session. * @return mixed * @throws ConfigurationKeyException */ - public function &val( $node, $returnRef = false ) { + public function val( $path ) { /* * Magic "/" returns the entire configuration tree. * @@ -122,34 +119,22 @@ * Note: Never log this tree insecurely, it will contain processor * credentials and other sensitive information. */ - if ( $node === '/' ) { - if ( $returnRef ) { - // TODO: Don't offer a return-by-reference. - $options = &$this->options; - } else { - $options = $this->options; - } - return $options; + if ( $path === '/' ) { + return $this->options; } - $keys = explode( '/', $node ); + $segments = explode( '/', $path ); - $croot = & $this->options; - foreach ( $keys as $key ) { - if ( array_key_exists( $key, $croot ) ) { - $croot = & $croot[ $key ]; + $currentNode = $this->options; + foreach ( $segments as $segment ) { + if ( array_key_exists( $segment, $currentNode ) ) { + $currentNode = $currentNode[$segment]; } else { - throw new ConfigurationKeyException( "Configuration key '{$node}' does not exist.", $node ); + throw new ConfigurationKeyException( "Configuration key '{$path}' does not exist.", $path ); } } - if ( $returnRef ) { - return $croot; - } else { - // Dereference the variable - $obj = $croot; - return $obj; - } + return $currentNode; } /** @@ -208,7 +193,7 @@ */ public function nodeExists( $node ) { try { - $this->val( $node, true ); + $this->val( $node ); return true; } catch ( ConfigurationKeyException $ex ) { return false; diff --git a/wikimedia/smash-pig/Core/DataFiles/AuditParser.php b/wikimedia/smash-pig/Core/DataFiles/AuditParser.php new file mode 100644 index 0000000..5dd777e --- /dev/null +++ b/wikimedia/smash-pig/Core/DataFiles/AuditParser.php @@ -0,0 +1,14 @@ +<?php + +namespace SmashPig\Core\DataFiles; + +interface AuditParser { + + /** + * Parse an audit file and normalize records + * + * @param string $path Full path of the file to be parsed + * @return array of donation, refund, and chargeback records + */ + public function parseFile( $path ); +} diff --git a/wikimedia/smash-pig/Core/DataStores/QueueWrapper.php b/wikimedia/smash-pig/Core/DataStores/QueueWrapper.php index ac504f6..537038d 100644 --- a/wikimedia/smash-pig/Core/DataStores/QueueWrapper.php +++ b/wikimedia/smash-pig/Core/DataStores/QueueWrapper.php @@ -30,7 +30,7 @@ $key = "data-store/$queueName"; // Examine the config node for a queue name - $node = $config->val( $key, true ); + $node = $config->val( $key ); if ( empty( $node['constructor-parameters'] ) || empty( $node['constructor-parameters'][0]['queue'] ) @@ -52,4 +52,4 @@ return $config->object( $key ); } -} \ No newline at end of file +} diff --git a/wikimedia/smash-pig/Core/Listeners/ListenerBase.php b/wikimedia/smash-pig/Core/Listeners/ListenerBase.php index 2c32225..9d35737 100644 --- a/wikimedia/smash-pig/Core/Listeners/ListenerBase.php +++ b/wikimedia/smash-pig/Core/Listeners/ListenerBase.php @@ -49,7 +49,7 @@ */ protected function validateRemoteIp() { // Obtain whitelist - $whitelist = $this->c->val( 'security/ip-whitelist', true ); + $whitelist = $this->c->val( 'security/ip-whitelist' ); // Obtain remote party IP $remote_ip = $this->request->getClientIp(); diff --git a/wikimedia/smash-pig/PaymentProviders/Adyen/Audit/AdyenAudit.php b/wikimedia/smash-pig/PaymentProviders/Adyen/Audit/AdyenAudit.php index 3ea8745..e30a79d 100644 --- a/wikimedia/smash-pig/PaymentProviders/Adyen/Audit/AdyenAudit.php +++ b/wikimedia/smash-pig/PaymentProviders/Adyen/Audit/AdyenAudit.php @@ -1,6 +1,8 @@ <?php namespace SmashPig\PaymentProviders\Adyen\Audit; use OutOfBoundsException; +use SmashPig\Core\DataFiles\AuditParser; +use SmashPig\Core\Logging\Logger; use SmashPig\Core\NormalizationException; use SmashPig\Core\UtcDate; use SmashPig\PaymentProviders\Adyen\ReferenceData; @@ -12,11 +14,41 @@ * Sends donations, chargebacks, and refunds to queue. * https://docs.adyen.com/manuals/reporting-manual/settlement-detail-report-structure/settlement-detail-report-journal-types */ -class AdyenAudit { +class AdyenAudit implements AuditParser { - protected $columnHeaders; - protected $ignoredStatuses; - protected $fileData = array(); + protected $columnHeaders = array( + 'Company Account', + 'Merchant Account', + 'Psp Reference', + 'Merchant Reference', + 'Payment Method', + 'Creation Date', + 'TimeZone', + 'Type', + 'Modification Reference', + 'Gross Currency', + 'Gross Debit (GC)', + 'Gross Credit (GC)', + 'Exchange Rate', + 'Net Currency', + 'Net Debit (NC)', + 'Net Credit (NC)', + 'Commission (NC)', + 'Markup (NC)', + 'Scheme Fees (NC)', + 'Interchange (NC)', + 'Payment Method Variant', + 'Modification Merchant Reference', + 'Batch Number', + 'Reserved4', + 'Reserved5', + 'Reserved6', + 'Reserved7', + 'Reserved8', + 'Reserved9', + 'Reserved10', + ); + protected static $ignoredTypes = array( 'fee', 'misccosts', @@ -38,52 +70,18 @@ 'paidoutreversed', ); - public function __construct() { - $this->columnHeaders = array( - 'Company Account', - 'Merchant Account', - 'Psp Reference', - 'Merchant Reference', - 'Payment Method', - 'Creation Date', - 'TimeZone', - 'Type', - 'Modification Reference', - 'Gross Currency', - 'Gross Debit (GC)', - 'Gross Credit (GC)', - 'Exchange Rate', - 'Net Currency', - 'Net Debit (NC)', - 'Net Credit (NC)', - 'Commission (NC)', - 'Markup (NC)', - 'Scheme Fees (NC)', - 'Interchange (NC)', - 'Payment Method Variant', - 'Modification Merchant Reference', - 'Batch Number', - 'Reserved4', - 'Reserved5', - 'Reserved6', - 'Reserved7', - 'Reserved8', - 'Reserved9', - 'Reserved10', - ); - } + protected $fileData; - // TODO base class this? public function parseFile( $path ) { - $this->path = $path; - $this->file = fopen( $path, 'r' ); + $this->fileData = array(); + $file = fopen( $path, 'r' ); $ignoreLines = 1; for ( $i = 0; $i < $ignoreLines; $i++ ) { - fgets( $this->file ); + fgets( $file ); } - while ( $line = fgetcsv( $this->file, 0, ',', '"', '\\' ) ) { + while ( $line = fgetcsv( $file, 0, ',', '"', '\\' ) ) { try { $this->parseLine( $line ); } catch ( NormalizationException $ex ) { @@ -91,7 +89,7 @@ Logger::error( $ex->getMessage() ); } } - fclose( $this->file ); + fclose( $file ); return $this->fileData; } diff --git a/wikimedia/smash-pig/PaymentProviders/Adyen/ReferenceData.php b/wikimedia/smash-pig/PaymentProviders/Adyen/ReferenceData.php index f4c5713..c973f7a 100644 --- a/wikimedia/smash-pig/PaymentProviders/Adyen/ReferenceData.php +++ b/wikimedia/smash-pig/PaymentProviders/Adyen/ReferenceData.php @@ -4,7 +4,7 @@ class ReferenceData { - static $methods = array( + protected static $methods = array( 'alipay' => array( 'method' => 'ew', 'submethod' => 'ew_alipay', diff --git a/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/AuditParser.php b/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/AmazonAudit.php similarity index 84% rename from wikimedia/smash-pig/PaymentProviders/Amazon/Audit/AuditParser.php rename to wikimedia/smash-pig/PaymentProviders/Amazon/Audit/AmazonAudit.php index d474c89..2d9db5d 100644 --- a/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/AuditParser.php +++ b/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/AmazonAudit.php @@ -1,11 +1,12 @@ <?php namespace SmashPig\PaymentProviders\Amazon\Audit; use SmashPig\Core\Context; +use SmashPig\Core\DataFiles\AuditParser; /** * Parses off-Amazon payments reports retrieved from MWS */ -class AuditParser { +class AmazonAudit implements AuditParser { public function parseFile( $path ) { $config = Context::get()->getProviderConfiguration(); diff --git a/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/RefundReport.php b/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/RefundReport.php index 614eca0..7b745b7 100644 --- a/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/RefundReport.php +++ b/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/RefundReport.php @@ -10,13 +10,14 @@ * http://amazonpayments.s3.amazonaws.com/documents/Sample%20Settlement%20Report.pdf#page=25 */ class RefundReport { - protected $fileData = array(); + protected $fileData; public static function isMine( $filename ) { return preg_match( '/.*REFUND_DATA.*csv/', $filename ); } public function parse( $path ) { + $this->fileData = array(); $csv = new HeadedCsvReader( $path, ',', 4096, 0 ); while ( $csv->valid() ) { diff --git a/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/SettlementReport.php b/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/SettlementReport.php index c96e28a..7fd3804 100644 --- a/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/SettlementReport.php +++ b/wikimedia/smash-pig/PaymentProviders/Amazon/Audit/SettlementReport.php @@ -12,13 +12,14 @@ */ class SettlementReport { - protected $fileData = array(); + protected $fileData; public static function isMine( $filename ) { return preg_match( '/.*SETTLEMENT_DATA.*csv/', $filename ); } public function parse( $path ) { + $this->fileData = array(); // Skip 5 lines at start of file; $csv = new HeadedCsvReader( $path, ',', 4096, 5 ); diff --git a/wikimedia/smash-pig/PaymentProviders/Amazon/Tests/phpunit/AuditTest.php b/wikimedia/smash-pig/PaymentProviders/Amazon/Tests/phpunit/AuditTest.php index 29f13b0..aca8e15 100644 --- a/wikimedia/smash-pig/PaymentProviders/Amazon/Tests/phpunit/AuditTest.php +++ b/wikimedia/smash-pig/PaymentProviders/Amazon/Tests/phpunit/AuditTest.php @@ -3,7 +3,7 @@ use SmashPig\Core\Context; use SmashPig\Tests\BaseSmashPigUnitTestCase; -use SmashPig\PaymentProviders\Amazon\Audit\AuditParser; +use SmashPig\PaymentProviders\Amazon\Audit\AmazonAudit; /** * Verify Amazon audit file processor functions @@ -20,7 +20,7 @@ * Normal donation */ public function testProcessDonation() { - $processor = new AuditParser(); + $processor = new AmazonAudit(); $output = $processor->parseFile( __DIR__ . '/../Data/audit/2015-10-01-SETTLEMENT_DATA_371273040777777.csv' ); $this->assertEquals( 1, count( $output ), 'Should have found one donation' ); $actual = $output[0]; @@ -42,7 +42,7 @@ * Now try a refund */ public function testProcessRefund() { - $processor = new AuditParser(); + $processor = new AmazonAudit(); $output = $processor->parseFile( __DIR__ . '/../Data/audit/2015-10-06-REFUND_DATA_414749300022222.csv' ); $this->assertEquals( 1, count( $output ), 'Should have found one refund' ); $actual = $output[0]; @@ -62,7 +62,7 @@ * And a chargeback */ public function testProcessChargeback() { - $processor = new AuditParser(); + $processor = new AmazonAudit(); $output = $processor->parseFile( __DIR__ . '/../Data/audit/2015-10-06-REFUND_DATA_414749300033333.csv' ); $this->assertEquals( 1, count( $output ), 'Should have found one chargeback' ); $actual = $output[0]; diff --git a/wikimedia/smash-pig/PaymentProviders/AstroPay/Audit/AstroPayAudit.php b/wikimedia/smash-pig/PaymentProviders/AstroPay/Audit/AstroPayAudit.php index dc19101..9ef92f2 100644 --- a/wikimedia/smash-pig/PaymentProviders/AstroPay/Audit/AstroPayAudit.php +++ b/wikimedia/smash-pig/PaymentProviders/AstroPay/Audit/AstroPayAudit.php @@ -1,59 +1,55 @@ <?php namespace SmashPig\PaymentProviders\AstroPay\Audit; use OutOfBoundsException; +use SmashPig\Core\DataFiles\AuditParser; use SmashPig\Core\Logging\Logger; use SmashPig\Core\NormalizationException; use SmashPig\Core\UtcDate; use SmashPig\PaymentProviders\AstroPay\ReferenceData; -class AstroPayAudit { +class AstroPayAudit implements AuditParser { - protected $columnHeaders; - protected $ignoredStatuses; - protected $fileData = array(); - protected $file; + protected $columnHeaders = array( + 'Type', // 'Payment' or 'Refund' + 'Creation date', // YYYY-MM-dd HH:mm:ss + 'Settlement date', // same format + 'Reference', // gateway_trxn_id + 'Invoice', // ct_id.attempt_num + 'Country', + 'Payment Method', // corresponds to our payment_submethod + 'Payment Method Type', // our payment_method + 'Net Amount (local)', + 'Amount (USD)', // gross, including fee + 'currency', // yup, this one is lower case + 'Status', + 'User Mail', + // These two fields refer to the original donation for refunds + 'Transaction Reference', + 'Transaction Invoice', + 'Fee', // In USD. AstroPay's processing fee + 'IOF', // In USD. Fee for financial transactions in Brazil + // The IOF is included in AstroPay's fee, but broken out by request + ); - public function __construct() { - $this->columnHeaders = array( - 'Type', // 'Payment' or 'Refund' - 'Creation date', // YYYY-MM-dd HH:mm:ss - 'Settlement date', // same format - 'Reference', // gateway_trxn_id - 'Invoice', // ct_id.attempt_num - 'Country', - 'Payment Method', // corresponds to our payment_submethod - 'Payment Method Type', // our payment_method - 'Net Amount (local)', - 'Amount (USD)', // gross, including fee - 'currency', // yup, this one is lower case - 'Status', - 'User Mail', - // These two fields refer to the original donation for refunds - 'Transaction Reference', - 'Transaction Invoice', - 'Fee', // In USD. AstroPay's processing fee - 'IOF', // In USD. Fee for financial transactions in Brazil - // The IOF is included in AstroPay's fee, but broken out by request - ); - // We don't need do anything with some audit lines - $this->ignoredStatuses = array( - 'Cancelled', // User pressed cancel or async payment expired - 'In process', // Chargeback is... charging back? 'Settled' means done - 'Reimbursed', // Chargeback settled in our favor - not refunding - 'Waiting Details', // Refund is in limbo; we'll wait for 'Completed' - ); - } + protected $ignoredStatuses = array( + 'Cancelled', // User pressed cancel or async payment expired + 'In process', // Chargeback is... charging back? 'Settled' means done + 'Reimbursed', // Chargeback settled in our favor - not refunding + 'Waiting Details', // Refund is in limbo; we'll wait for 'Completed' + ); + + protected $fileData; public function parseFile( $path ) { - $this->path = $path; - $this->file = fopen( $path, 'r' ); + $this->fileData = array(); + $file = fopen( $path, 'r' ); $ignoreLines = 1; for ( $i = 0; $i < $ignoreLines; $i++ ) { - fgets( $this->file ); + fgets( $file ); } - while ( $line = fgetcsv( $this->file, 0, ';', '"', '\\' ) ) { + while ( $line = fgetcsv( $file, 0, ';', '"', '\\' ) ) { try { $this->parseLine( $line ); } catch ( NormalizationException $ex ) { @@ -61,7 +57,7 @@ Logger::error( $ex->getMessage() ); } } - fclose( $this->file ); + fclose( $file ); return $this->fileData; } diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/Audit/IngenicoAudit.php b/wikimedia/smash-pig/PaymentProviders/Ingenico/Audit/IngenicoAudit.php new file mode 100644 index 0000000..2adc816 --- /dev/null +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/Audit/IngenicoAudit.php @@ -0,0 +1,199 @@ +<?php namespace SmashPig\PaymentProviders\Ingenico\Audit; + +use DOMDocument; +use DOMElement; +use RuntimeException; +use SmashPig\Core\DataFiles\AuditParser; +use SmashPig\Core\Logging\Logger; +use SmashPig\Core\UtcDate; +use SmashPig\PaymentProviders\Ingenico\ReferenceData; + +class IngenicoAudit implements AuditParser { + + protected $fileData; + + protected $donationMap = array( + 'PaymentAmount' => 'gross', + 'IPAddressCustomer' => 'user_ip', + 'BillingFirstname' => 'first_name', + 'BillingSurname' => 'last_name', + 'BillingStreet' => 'street_address', + 'BillingCity' => 'city', + 'ZipCode' => 'postal_code', + 'BillingCountryCode' => 'country', + 'BillingEmail' => 'email', + 'AdditionalReference' => 'contribution_tracking_id', + 'PaymentProductId' => 'gc_product_id', + 'OrderID' => 'order_id', + 'PaymentCurrency' => 'currency', + 'AmountLocal' => 'gross', + 'TransactionDateTime' => 'date', + ); + + protected $refundMap = array( + 'DebitedAmount' => 'gross', + 'AdditionalReference' => 'contribution_tracking_id', + 'OrderID' => 'gateway_parent_id', + 'DebitedCurrency' => 'gross_currency', + 'TransactionDateTime' => 'date', + ); + + protected $recordsWeCanDealWith = array( + // Credit card item that has been processed, but not settled. + // We take these seriously. + // TODO: Why aren't we waiting for +ON? + 'XON' => 'donation', + // Settled "Invoice Payment". Could be invoice, bt, rtbt, check, + // prepaid card, ew, cash + '+IP' => 'donation', + '-CB' => 'chargeback', // Credit card chargeback + '-CR' => 'refund', // Credit card refund + '+AP' => 'donation', // Direct Debit collected + ); + + public function parseFile( $path ) { + $this->fileData = array(); + $unzippedFullPath = $this->getUnzippedFile( $path ); + + // load the XML into a DOMDocument. + // Total Memory Hog Alert. Handle with care. + $domDoc = new DOMDocument( '1.0' ); + Logger::info( "Loading XML from $unzippedFullPath" ); + $domDoc->load( $unzippedFullPath ); + unlink( $unzippedFullPath ); + Logger::info( "Processing" ); + + foreach ( $domDoc->getElementsByTagName( 'DataRecord' ) as $recordNode ) { + $this->parseRecord( $recordNode ); + } + + return $this->fileData; + } + + protected function parseRecord( DOMElement $recordNode ) { + $category = $recordNode->getElementsByTagName( 'Recordcategory' ) + ->item( 0 )->nodeValue; + $type = $recordNode->getElementsByTagName( 'Recordtype' ) + ->item( 0 )->nodeValue; + + $compoundType = $category . $type; + if ( !array_key_exists( $compoundType, $this->recordsWeCanDealWith ) ) { + return; + } + + if ( $category === '-' ) { + $refundType = $this->recordsWeCanDealWith[$compoundType]; + $record = $this->parseRefund( $recordNode, $refundType ); + } else { + $record = $this->parseDonation( $recordNode ); + } + $record = $this->normalizeValues( $record ); + // TODO: label Connect API donations as 'ingenico' + $record['gateway'] = 'globalcollect'; + + $this->fileData[] = $record; + } + + protected function parseDonation( DOMElement $recordNode ) { + $record = $this->xmlToArray( $recordNode, $this->donationMap ); + $record['gateway_txn_id'] = $record['order_id']; + $record = $this->addPaymentMethod( $record ); + return $record; + } + + protected function parseRefund( DOMElement $recordNode, $type ) { + $record = $this->xmlToArray( $recordNode, $this->refundMap ); + $record['type'] = $type; + return $record; + } + + protected function xmlToArray( DOMElement $recordNode, $map ) { + $record = array(); + foreach ( $map as $theirs => $ours ) { + foreach ( $recordNode->getElementsByTagName( $theirs ) as $recordItem ) { + $record[$ours] = $recordItem->nodeValue; // there 'ya go: Normal already. + } + } + return $record; + } + + /** + * Adds our normalized payment_method and payment_submethod params based + * on the codes that GC uses + * + * @param array $record The record from the wx file, in array format + * @return array The $record param with our normal keys appended + */ + function addPaymentMethod( $record ) { + $normalized = ReferenceData::decodePaymentMethod( + $record['gc_product_id'] + ); + $record = array_merge( $record, $normalized ); + + unset ( $record['gc_product_id'] ); + return $record; + } + + /** + * @param string $path Path to original zipped file + * @return string Path to unzipped file in working directory + */ + protected function getUnzippedFile( $path ) { + $zippedParts = explode( DIRECTORY_SEPARATOR, $path ); + $zippedFilename = array_pop( $zippedParts ); + // TODO keep unzipped files around? + $workingDirectory = tempnam( sys_get_temp_dir(), 'ingenico_audit' ); + if ( file_exists( $workingDirectory ) ) { + unlink( $workingDirectory ); + } + mkdir( $workingDirectory ); + // whack the .gz on the end + $unzippedFilename = substr( $zippedFilename, 0, strlen( $zippedFilename ) - 3 ); + + $copiedZipPath = $workingDirectory . DIRECTORY_SEPARATOR . $zippedFilename; + copy( $path, $copiedZipPath ); + if ( !file_exists( $copiedZipPath ) ) { + throw new RuntimeException( + "FILE PROBLEM: Trying to copy $path to $copiedZipPath " . + 'for decompression, and something went wrong' + ); + } + + $unzippedFullPath = $workingDirectory . DIRECTORY_SEPARATOR . $unzippedFilename; + // decompress + Logger::info( "Gunzipping $copiedZipPath" ); + // FIXME portability + $cmd = "gunzip -f $copiedZipPath"; + exec( escapeshellcmd( $cmd ) ); + + // now check to make sure the file you expect actually exists + if ( !file_exists( $unzippedFullPath ) ) { + throw new RuntimeException( + 'FILE PROBLEM: Something went wrong with decompressing WX file: ' . + "$cmd : $unzippedFullPath doesn't exist." + ); + } + return $unzippedFullPath; + } + + /** + * Normalize amounts, dates, and IDs to match everything else in SmashPig + * FIXME: do this with transformers migrated in from DonationInterface + * + * @param array $record + * @return array The record, with values normalized + */ + protected function normalizeValues( $record ) { + if ( isset( $record['gross'] ) ) { + $record['gross'] = $record['gross'] / 100; + } + if ( isset( $record['contribution_tracking_id'] ) ) { + $parts = explode( '.', $record['contribution_tracking_id'] ); + $record['contribution_tracking_id'] = $parts[0]; + } + if ( isset( $record['date'] ) ) { + $record['date'] = UtcDate::getUtcTimestamp( $record['date'] ); + } + return $record; + } +} diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/HostedCheckoutProvider.php b/wikimedia/smash-pig/PaymentProviders/Ingenico/HostedCheckoutProvider.php index 6f67e8b..5047135 100644 --- a/wikimedia/smash-pig/PaymentProviders/Ingenico/HostedCheckoutProvider.php +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/HostedCheckoutProvider.php @@ -28,6 +28,12 @@ function getHostedPaymentUrl($partialRedirectUrl) { return "https://{$this->subdomain}.$partialRedirectUrl"; } + + function getHostedPaymentStatus($hostedPaymentId){ + $path = "hostedcheckouts/$hostedPaymentId"; + $response = $this->api->makeApiCall($path, 'GET'); + return $response; + } } diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/IngenicoPaymentProvider.php b/wikimedia/smash-pig/PaymentProviders/Ingenico/IngenicoPaymentProvider.php index 5d39ba7..e30a10e 100644 --- a/wikimedia/smash-pig/PaymentProviders/Ingenico/IngenicoPaymentProvider.php +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/IngenicoPaymentProvider.php @@ -18,4 +18,10 @@ $this->providerConfiguration = Context::get()->getProviderConfiguration(); $this->api = $this->providerConfiguration->object( 'api' ); } + + public function getPaymentStatus($paymentId){ + $path = "payments/$paymentId"; + $response = $this->api->makeApiCall($path, 'GET'); + return $response; + } } diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/ReferenceData.php b/wikimedia/smash-pig/PaymentProviders/Ingenico/ReferenceData.php new file mode 100644 index 0000000..b2f69c4 --- /dev/null +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/ReferenceData.php @@ -0,0 +1,58 @@ +<?php + +namespace SmashPig\PaymentProviders\Ingenico; + +use OutOfBoundsException; + +class ReferenceData { + // FIXME: replace this whole class with payment_(sub)method.yaml files + + protected static $methods = array( + '1' => array( 'payment_method' => 'cc', 'payment_submethod' => 'visa' ), + '2' => array( 'payment_method' => 'cc', 'payment_submethod' => 'amex' ), + '3' => array( 'payment_method' => 'cc', 'payment_submethod' => 'mc' ), + '11' => array( 'payment_method' => 'bt', 'payment_submethod' => 'bt' ), + '117' => array( 'payment_method' => 'cc', 'payment_submethod' => 'maestro' ), + '118' => array( 'payment_method' => 'cc', 'payment_submethod' => 'solo' ), + '124' => array( 'payment_method' => 'cc', 'payment_submethod' => 'laser' ), + '125' => array( 'payment_method' => 'cc', 'payment_submethod' => 'jcb' ), + '128' => array( 'payment_method' => 'cc', 'payment_submethod' => 'discover' ), + '130' => array( 'payment_method' => 'cc', 'payment_submethod' => 'cb' ), + '500' => array( 'payment_method' => 'obt', 'payment_submethod' => 'bpay' ), + '701' => array( 'payment_method' => 'dd', 'payment_submethod' => 'dd_nl' ), + '702' => array( 'payment_method' => 'dd', 'payment_submethod' => 'dd_de' ), + '703' => array( 'payment_method' => 'dd', 'payment_submethod' => 'dd_at' ), + '704' => array( 'payment_method' => 'dd', 'payment_submethod' => 'dd_fr' ), + '705' => array( 'payment_method' => 'dd', 'payment_submethod' => 'dd_gb' ), + '706' => array( 'payment_method' => 'dd', 'payment_submethod' => 'dd_be' ), + '707' => array( 'payment_method' => 'dd', 'payment_submethod' => 'dd_ch' ), + '708' => array( 'payment_method' => 'dd', 'payment_submethod' => 'dd_it' ), + '709' => array( 'payment_method' => 'dd', 'payment_submethod' => 'dd_es' ), + '805' => array( 'payment_method' => 'rtbt', 'payment_submethod' => 'rtbt_nordea_sweden' ), + '809' => array( 'payment_method' => 'rtbt', 'payment_submethod' => 'rtbt_ideal' ), + '810' => array( 'payment_method' => 'rtbt', 'payment_submethod' => 'rtbt_enets' ), + '836' => array( 'payment_method' => 'rtbt', 'payment_submethod' => 'rtbt_sofortuberweisung' ), + '840' => array( 'payment_method' => 'ew', 'payment_submethod' => 'ew_paypal' ), + '841' => array( 'payment_method' => 'ew', 'payment_submethod' => 'ew_webmoney' ), + '843' => array( 'payment_method' => 'ew', 'payment_submethod' => 'ew_moneybookers' ), + '845' => array( 'payment_method' => 'ew', 'payment_submethod' => 'ew_cashu' ), + '849' => array( 'payment_method' => 'ew', 'payment_submethod' => 'ew_yandex' ), + '856' => array( 'payment_method' => 'rtbt', 'payment_submethod' => 'rtbt_eps' ), + '861' => array( 'payment_method' => 'ew', 'payment_submethod' => 'ew_alipay' ), + '1503' => array( 'payment_method' => 'cash', 'payment_submethod' => 'cash_boleto' ), + ); + + /** + * Gets our normalized payment_method and payment_submethod params from the + * codes that GC uses + * + * @param string $paymentProductId + * @return array containing payment_method and payment_submethod + */ + public static function decodePaymentMethod( $paymentProductId ) { + if ( !array_key_exists( $paymentProductId, self::$methods ) ) { + throw new OutOfBoundsException( "Unknown Payment Product ID $paymentProductId " ); + } + return self::$methods[$paymentProductId]; + } +} diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/chargeback.xml.gz b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/chargeback.xml.gz new file mode 100644 index 0000000..b38a947 --- /dev/null +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/chargeback.xml.gz Binary files differ diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/donation.xml.gz b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/donation.xml.gz new file mode 100644 index 0000000..ebb1109 --- /dev/null +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/donation.xml.gz Binary files differ diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/hostedPaymentStatus.response b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/hostedPaymentStatus.response new file mode 100644 index 0000000..9d35135 --- /dev/null +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/hostedPaymentStatus.response @@ -0,0 +1,51 @@ +HTTP/1.1 200 OK +Date: Mon, 30 Jan 2017 17:58:02 GMT +Server: Apache/2.4.16 (Unix) OpenSSL/1.0.1t +X-Powered-By: Servlet/3.0 JSP/2.2 +Transfer-Encoding: chunked +Content-Type: application/json + +{ + "createdPaymentOutput": { + "payment": { + "id": "000000891566072501680000200001", + "paymentOutput": { + "amountOfMoney": { + "amount": 2345, + "currencyCode": "USD" + }, + "references": { + "paymentReference": "0" + }, + "paymentMethod": "card", + "cardPaymentMethodSpecificOutput": { + "paymentProductId": 1, + "authorisationCode": "123456", + "card": { + "cardNumber": "************7977", + "expiryDate": "1220" + }, + "fraudResults": { + "avsResult": "0", + "cvvResult": "M", + "fraudServiceResult": "no-advice" + } + } + }, + "status": "PENDING_APPROVAL", + "statusOutput": { + "isCancellable": true, + "statusCode": 600, + "statusCodeChangeDateTime": "20140717145840", + "isAuthorized": true + } + }, + "paymentCreationReferences": { + "additionalReference": "00000089156607250168", + "externalReference": "000000891566072501680000200001" + }, + "tokens": "" + }, + "status": "PAYMENT_CREATED" +} + diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/paymentStatus.response b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/paymentStatus.response new file mode 100644 index 0000000..216a1cb --- /dev/null +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/paymentStatus.response @@ -0,0 +1,43 @@ +HTTP/1.1 200 OK +Date: Mon, 30 Jan 2017 17:58:02 GMT +Server: Apache/2.4.16 (Unix) OpenSSL/1.0.1t +X-Powered-By: Servlet/3.0 JSP/2.2 +Transfer-Encoding: chunked +Content-Type: application/json + +{ + "id": "000000850010000188180000200001", + "paymentOutput": + { + "amountOfMoney": + { + "amount": 100, + "currencyCode": "EUR" + }, + "references": { + "merchantReference": "AcmeOrder0001", + "paymentReference": "0" + }, + "paymentMethod": "card", + "cardPaymentMethodSpecificOutput": { + "paymentProductId": 1, + "authorisationCode": "726747", + "card": { + "cardNumber": "************7977", + "expiryDate": "1220" + }, + "fraudResults": { + "avsResult": "0", + "cvvResult": "0", + "fraudServiceResult": "no-advice" + } + } + }, + "status": "PENDING_APPROVAL", + "statusOutput": { + "isCancellable": true, + "statusCode": 600, + "statusCodeChangeDateTime": "20140630154921", + "isAuthorized": true + } +} diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/refund.xml.gz b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/refund.xml.gz new file mode 100644 index 0000000..8fc709c --- /dev/null +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/Data/refund.xml.gz Binary files differ diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/AuditTest.php b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/AuditTest.php new file mode 100644 index 0000000..3fe756c --- /dev/null +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/AuditTest.php @@ -0,0 +1,81 @@ +<?php + +namespace SmashPig\PaymentProviders\Ingenico\Tests; + +use SmashPig\PaymentProviders\Ingenico\Audit\IngenicoAudit; +use SmashPig\Tests\BaseSmashPigUnitTestCase; + +/** + * @group Audit + * @group Ingenico + */ +class AuditTest extends BaseSmashPigUnitTestCase { + /** + * Normal donation + */ + public function testProcessDonation() { + $processor = new IngenicoAudit(); + $output = $processor->parseFile( __DIR__ . '/../Data/donation.xml.gz' ); + $this->assertEquals( 1, count( $output ), 'Should have found one donation' ); + $actual = $output[0]; + $expected = array( + 'gateway' => 'globalcollect', // TODO: switch to ingenico for Connect + 'gross' => 3.00, + 'contribution_tracking_id' => '5551212', + 'currency' => 'USD', + 'order_id' => '987654321', + 'gateway_txn_id' => '987654321', + 'payment_method' => 'cc', + 'payment_submethod' => 'visa', + 'date' => 1501368968, + 'user_ip' => '111.222.33.44', + 'first_name' => 'Arthur', + 'last_name' => 'Aardvark', + 'street_address' => '1111 Fake St', + 'city' => 'Denver', + 'country' => 'US', + 'email' => 'dutch...@flying.net', + ); + $this->assertEquals( $expected, $actual, 'Did not parse donation correctly' ); + } + + /** + * Now try a refund + */ + public function testProcessRefund() { + $processor = new IngenicoAudit(); + $output = $processor->parseFile( __DIR__ . '/../Data/refund.xml.gz' ); + $this->assertEquals( 1, count( $output ), 'Should have found one refund' ); + $actual = $output[0]; + $expected = array( + 'gateway' => 'globalcollect', // TODO: switch to ingenico for Connect + 'contribution_tracking_id' => '5551212', + 'date' => 1500942220, + 'gross' => 100, + 'gateway_parent_id' => '123456789', + 'gross_currency' => 'USD', + 'type' => 'refund', + ); + $this->assertEquals( $expected, $actual, 'Did not parse refund correctly' ); + } + + /** + * And a chargeback + */ + public function testProcessChargeback() { + $processor = new IngenicoAudit(); + $output = $processor->parseFile( __DIR__ . '/../Data/chargeback.xml.gz' ); + $this->assertEquals( 1, count( $output ), 'Should have found one chargeback' ); + $actual = $output[0]; + $expected = array( + 'gateway' => 'globalcollect', // TODO: switch to ingenico for Connect + 'contribution_tracking_id' => '5551212', + 'date' => 1495023569, + 'gross' => 200, + 'gateway_parent_id' => '5167046621', + 'gross_currency' => 'USD', + 'type' => 'chargeback', + ); + $this->assertEquals( $expected, $actual, 'Did not parse chargeback correctly' ); + } +} diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/HostedCheckoutProviderTest.php b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/HostedCheckoutProviderTest.php index b504893..96c52bb 100644 --- a/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/HostedCheckoutProviderTest.php +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/HostedCheckoutProviderTest.php @@ -2,6 +2,7 @@ namespace SmashPig\PaymentProviders\Ingenico\Tests; +use PHPUnit_Framework_MockObject_MockObject; use SmashPig\PaymentProviders\Ingenico\HostedCheckoutProvider; use SmashPig\Tests\BaseSmashPigUnitTestCase; @@ -61,4 +62,17 @@ $expectedUrl = 'https://payments.test.' . $partialRedirectUrl; $this->assertEquals($expectedUrl, $hostedPaymentUrl); } + + public function testGetHostedPaymentStatus(){ + $hostedPaymentId = '8915-28e5b79c889641c8ba770f1ba576c1fe'; + $this->setUpResponse(__DIR__ . "/../Data/hostedPaymentStatus.response", 200); + $this->curlWrapper->expects( $this->once() ) + ->method( 'execute' )->with( + $this->equalTo("https://api-sandbox.globalcollect.com/v1/1234/hostedcheckouts/$hostedPaymentId"), + $this->equalTo('GET') + ); + $response = $this->provider->getHostedPaymentStatus($hostedPaymentId); + $this->assertEquals('PAYMENT_CREATED', $response['status']); + + } } diff --git a/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/IngenicoPaymentProviderTest.php b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/IngenicoPaymentProviderTest.php new file mode 100644 index 0000000..eaa54a6 --- /dev/null +++ b/wikimedia/smash-pig/PaymentProviders/Ingenico/Tests/phpunit/IngenicoPaymentProviderTest.php @@ -0,0 +1,35 @@ +<?php + +namespace SmashPig\PaymentProviders\Ingenico\Tests; + +use PHPUnit_Framework_MockObject_MockObject; +use SmashPig\PaymentProviders\Ingenico\IngenicoPaymentProvider; +use SmashPig\Tests\BaseSmashPigUnitTestCase; + +/** + * @group Ingenico + */ +class IngenicoPaymentProviderTest extends BaseSmashPigUnitTestCase { + /** + * @var PHPUnit_Framework_MockObject_MockObject + */ + protected $provider; + + public function setUp() { + parent::setUp(); + $this->setProviderConfiguration( 'ingenico' ); + $this->provider = $this->getMockForAbstractClass('\SmashPig\PaymentProviders\Ingenico\IngenicoPaymentProvider'); + } + + public function testGetPaymentStatus(){ + $paymentId = '000000850010000188180000200001'; + $this->setUpResponse(__DIR__ . '/../Data/paymentStatus.response', 200); + $this->curlWrapper->expects( $this->once() ) + ->method( 'execute' )->with( + $this->equalTo("https://api-sandbox.globalcollect.com/v1/1234/payments/$paymentId"), + $this->equalTo('GET') + ); + $response = $this->provider->getPaymentStatus($paymentId); + $this->assertEquals($paymentId, $response['id']); + } +} \ No newline at end of file diff --git a/wikimedia/smash-pig/PaymentProviders/PayPal/Tests/Data/recurring_payment_profile_created_transformed.json b/wikimedia/smash-pig/PaymentProviders/PayPal/Tests/Data/recurring_payment_profile_created_transformed.json index ce73c80..0c7ad8e 100644 --- a/wikimedia/smash-pig/PaymentProviders/PayPal/Tests/Data/recurring_payment_profile_created_transformed.json +++ b/wikimedia/smash-pig/PaymentProviders/PayPal/Tests/Data/recurring_payment_profile_created_transformed.json @@ -15,5 +15,7 @@ "start_date": 1492533928, "date": 1492533928, "gateway": "paypal_ec", - "recurring": "1" + "recurring": "1", + "gross": "140", + "currency": "JPY" } diff --git a/wikimedia/smash-pig/config/paypal/main.yaml b/wikimedia/smash-pig/config/paypal/main.yaml index cbd7aaf..8ee159f 100644 --- a/wikimedia/smash-pig/config/paypal/main.yaml +++ b/wikimedia/smash-pig/config/paypal/main.yaml @@ -28,6 +28,7 @@ # FIXME This is only true for refund messages. Where to represent? #txn_id: gateway_refund_id mc_currency: currency + currency_code: currency # FIXME rename refund_type reason_code: type #test_ipn: # signals test mode @@ -46,6 +47,9 @@ # FIXME this too address_name: supplemental_address_1 gateway: gateway + # NOTE: order matters. When multiple PayPal fields map to the + # same SmashPig field, PayPal fields listed later take precedence. + amount_per_cycle: gross mc_gross: gross mc_amount3: gross amount3: gross -- To view, visit https://gerrit.wikimedia.org/r/370091 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: If480186992d97fc1ef5748e919a24c0f16d4e662 Gerrit-PatchSet: 1 Gerrit-Project: wikimedia/fundraising/crm/vendor Gerrit-Branch: master Gerrit-Owner: Ejegg <ej...@ejegg.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits