diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..6b71fc789360605b77f8f82161887ac88953f392 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +.git/ export-ignore +tests/ export-ignore +vendor/ export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.gitlab-ci.yml export-ignore +.idea export-ignore +apigen.neon export-ignore +phpcs.xml.dist export-ignore +phpunit-integration.xml export-ignore +phpunit-unit.xml export-ignore diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2c2659b47d3f4d71406a73bdb9a462a80f3064f0..538bcc9cea14c56fc7380de1cd75edd5f0ca07d7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,86 +1,7 @@ variables: - PHP_ERROR_REPORTING: E_ALL - COMPOSER_ALLOW_SUPERUSER: 1 - GIT_STRATEGY: fetch - MYSQL_ROOT_PASSWORD: mysql - MYSQL_DATABASE: wptest - MYSQL_USER: mysql - MYSQL_PASSWORD: mysql - MYSQL_INNODB_LOG_BUFFER_SIZE: 32M - PHP_ERROR_REPORTING: E_ALL - COMPOSER_ALLOW_SUPERUSER: 1 - GIT_STRATEGY: fetch + DISABLE_FUNCTIONAL: 1 + DISABLE_ACCEPTANCE: 1 + IS_LIBRARY: 1 + DISABLE_PHP_5_5: 1 - -stages: - - tools - - tests - -.template: &job-test-template - stage: tests - coverage: '/^\s*Lines:\s*\d+.\d+\%/' - -.template: &job-test-unit-template - <<: *job-test-template - script: - - echo ${WPDESK_CI_VERSION} - - ls -l - - php --version - - cat /tmp/wordpress-develop/src/wp-includes/version.php - - cat /tmp/woocommerce/woocommerce.php - - composer update --no-progress - - vendor/bin/phpunit --configuration phpunit-unit.xml --coverage-text --colors=never - -.template: &job-test-integration-template - <<: *job-test-template - services: - - mysql:5.6 - script: - - echo ${WPDESK_CI_VERSION} - - ls -l - - php --version - - cat /tmp/wordpress-develop/src/wp-includes/version.php - - cat /tmp/woocommerce/woocommerce.php - - composer update --no-progress - - if [[ -f tests/integration/prepare.sh ]]; then sh tests/integration/prepare.sh; fi - - vendor/bin/phpunit --configuration phpunit-integration.xml --coverage-text --colors=never - - -before_script: - - cd ${CI_PROJECT_DIR} - -phpmetric metrics: - stage: tools - image: wpdesknet/phpunit-woocommerce:0-0 - allow_failure: true - when: manual - artifacts: - when: always - expire_in: 1 month - name: "metrics" - paths: - - ${CI_PROJECT_DIR}/phpmetric - script: - - echo ${WPDESK_CI_VERSION} - - composer require phpmetrics/phpmetrics - - composer update --no-progress - - php ./vendor/bin/phpmetrics --report-html=phpmetric . - -churn metrics: - stage: tools - image: wpdesknet/phpunit-woocommerce:0-0 - allow_failure: true - when: manual - script: - - echo ${WPDESK_CI_VERSION} - - composer require bmitch/churn-php - - composer update --no-progress - - vendor/bin/churn run classes inc - -#unit test lastest: -# <<: *job-test-unit-template -# image: wpdesknet/phpunit-woocommerce:0-0 - -integration test lastest: - <<: *job-test-integration-template - image: wpdesknet/phpunit-woocommerce:0-0 +include: 'https://gitlab.com/wpdesk/gitlab-ci/raw/master/gitlab-ci-1.2.yml' diff --git a/CHANGELOG.md b/CHANGELOG.md index fbf363700ca4ac607d02c4ff8d1d499f8ed65547..9f05dc829b8a847b3afc131b001b7723161473d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [1.4.0] - 2019-01-21 +### Changed +- WC integration now considers broken WC_Logger implementation +- Does not capture WC logger in WC < 3.5 + ## [1.3.1] - 2018-10-30 ### Changed - setDisableLog changes to disableLog diff --git a/composer.json b/composer.json index a548036d28e2707e3112c67c9be2195b11409b22..6e720b4254f95b9975b20f3876a0b507a374fbd2 100644 --- a/composer.json +++ b/composer.json @@ -12,10 +12,9 @@ "monolog/monolog": "^1.23" }, "require-dev": { - "phpunit/phpunit": "^6", + "phpunit/phpunit": "^5", "wp-coding-standards/wpcs": "^0.14.1", "squizlabs/php_codesniffer": "^3.0.2", - "10up/wp_mock": "^0.3", "wimg/php-compatibility": "^8" }, "autoload": { diff --git a/src/WC/WooCommerceCapture.php b/src/WC/WooCommerceCapture.php index 5878dddec63a7414c87d1626a037d21ed134558f..cbe5d101538845bfe8e95b007c3b2c9ec3f0fb74 100644 --- a/src/WC/WooCommerceCapture.php +++ b/src/WC/WooCommerceCapture.php @@ -15,6 +15,9 @@ class WooCommerceCapture const WOOCOMMERCE_LOGGER_FILTER = 'woocommerce_logging_class'; const WOOCOMMERCE_AFTER_IS_LOADED_ACTION = 'woocommerce_loaded'; + /** @var string Minimal version of WooCommerce supported by logger capture */ + const SUPPORTED_WC_VERSION = '3.5.0'; + /** * Is logger filter captured by library. * @@ -65,34 +68,49 @@ class WooCommerceCapture if ($this->captureHookFunction === null) { $this->captureHookFunction = function () use ($monolog) { - return new WooCommerceMonologPlugin($monolog); + return new WooCommerceMonologPlugin($monolog, $this->originalWCLogger); }; $this->monolog->pushHandler(new WooCommerceHandler($this->originalWCLogger)); } } + /** + * Is this version of WooCommerce is supported by logger capture + * + * @return bool + */ + public static function isSupportedWCVersion() + { + return version_compare(\WooCommerce::instance()->version, self::SUPPORTED_WC_VERSION, '>='); + } + /** * Capture WooCommerce logger and inject our decorated Logger */ public function captureWcLogger() { - if ($this->isCaptured) { - throw new WCLoggerAlreadyCaptured('Try to free wc logger first.'); - } - - if ($this->isWooCommerceLoggerAvailable()) { - $this->prepareFreeHookCallable(); - $this->prepareCaptureHookCallable(); - - remove_filter(self::WOOCOMMERCE_LOGGER_FILTER, $this->freeHookFunction); - add_filter(self::WOOCOMMERCE_LOGGER_FILTER, $this->captureHookFunction); - - $this->isCaptured = true; - } elseif (function_exists('add_action')) { - add_action(self::WOOCOMMERCE_AFTER_IS_LOADED_ACTION, [$this, 'captureWcLogger']); + if (self::isSupportedWCVersion()) { + if ($this->isCaptured) { + throw new WCLoggerAlreadyCaptured('Try to free wc logger first.'); + } + + if ($this->isWooCommerceLoggerAvailable()) { + $this->prepareFreeHookCallable(); + $this->prepareCaptureHookCallable(); + + remove_filter(self::WOOCOMMERCE_LOGGER_FILTER, $this->freeHookFunction); + add_filter(self::WOOCOMMERCE_LOGGER_FILTER, $this->captureHookFunction); + + $this->isCaptured = true; + } elseif (function_exists('add_action')) { + add_action(self::WOOCOMMERCE_AFTER_IS_LOADED_ACTION, [$this, 'captureWcLogger']); + } else { + $this->monolog->alert('Cannot capture WC - WordPress is not available.'); + } } else { - $this->monolog->alert('Cannot capture WC - WordPress is not available.'); + $this->monolog->alert('Cannot capture WC - WooCommerce version is not supported.'); } + } /** diff --git a/src/WC/WooCommerceMonologPlugin.php b/src/WC/WooCommerceMonologPlugin.php index 6bdccbb78f30ab28749cf1d67320d91c70abe8da..44a504c1b9096f9b3219bc5bbe64644bd339f1b6 100644 --- a/src/WC/WooCommerceMonologPlugin.php +++ b/src/WC/WooCommerceMonologPlugin.php @@ -12,14 +12,38 @@ use WC_Log_Levels; * * @package WPDesk\Logger */ -class WooCommerceMonologPlugin implements \WC_Logger_Interface { +class WooCommerceMonologPlugin implements \WC_Logger_Interface +{ - /** @var Logger */ - private $monolog; + /** @var Logger */ + private $monolog; - public function __construct( Logger $monolog ) { - $this->monolog = $monolog; - } + /** @var \WC_Logger */ + private $originalWCLogger; + + public function __construct(Logger $monolog, \WC_Logger $originalLogger) + { + $this->monolog = $monolog; + $this->originalWCLogger = $originalLogger; + } + + /** + * Method added for compatibility with \WC_Logger + * + * @param string $source + */ + public function clear($source = '') + { + $this->originalWCLogger->clear($source); + } + + /** + * Method added for compatibility with \WC_Logger + */ + public function clear_expired_logs() + { + $this->originalWCLogger->clear_expired_logs(); + } /** * Method for compatibility reason. Do not use. @@ -31,127 +55,129 @@ class WooCommerceMonologPlugin implements \WC_Logger_Interface { * * @deprecated */ - public function add( $handle, $message, $level = WC_Log_Levels::NOTICE ) { - $this->log($message, $level); - } - - /** - * System is unusable. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function emergency($message, $context = array()) - { - $this->log(LogLevel::EMERGENCY, $message, $context); - } - - public function log( $level, $message, $context = [] ) { - $this->monolog->log($level, $message, $context); - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function alert($message, $context = array()) - { - $this->log(LogLevel::ALERT, $message, $context); - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function critical($message, $context = array()) - { - $this->log(LogLevel::CRITICAL, $message, $context); - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function error($message, $context = array()) - { - $this->log(LogLevel::ERROR, $message, $context); - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function warning($message, $context = array()) - { - $this->log(LogLevel::WARNING, $message, $context); - } - - /** - * Normal but significant events. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function notice($message, $context = array()) - { - $this->log(LogLevel::NOTICE, $message, $context); - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function info($message, $context = array()) - { - $this->log(LogLevel::INFO, $message, $context); - } - - /** - * Detailed debug information. - * - * @param string $message - * @param array $context - * - * @return void - */ - public function debug($message, $context = array()) - { - $this->log(LogLevel::DEBUG, $message, $context); - } + public function add($handle, $message, $level = WC_Log_Levels::NOTICE) + { + $this->log($message, $level); + } + + /** + * System is unusable. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function emergency($message, $context = array()) + { + $this->log(LogLevel::EMERGENCY, $message, $context); + } + + public function log($level, $message, $context = []) + { + $this->monolog->log($level, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } } diff --git a/tests/integration/TestWPDeskLoggerFactory.php b/tests/integration/TestWPDeskLoggerFactory.php index 21fc459c3c07af81c5d44d1bc67a3b00f27440a8..3db326cad8f1f860e14b466c9703290db700af03 100644 --- a/tests/integration/TestWPDeskLoggerFactory.php +++ b/tests/integration/TestWPDeskLoggerFactory.php @@ -3,6 +3,7 @@ use Monolog\Handler\AbstractHandler; use Monolog\Logger; use PHPUnit\Framework\Error\Notice; +use WPDesk\Logger\WC\WooCommerceCapture; use WPDesk\Logger\WC\WooCommerceMonologPlugin; use WPDesk\Logger\WPDeskLoggerFactory; @@ -30,20 +31,26 @@ class TestWPDeskLoggerFactory extends WP_UnitTestCase public function testWCLoggingIsCapturedByOurLogs() { - (new WPDeskLoggerFactory())->createWPDeskLogger(); - $this->assertInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), "Logger should be captured."); - } - - public function testOurLogsGetAllErrors() - { - $someMessage = 'whatever'; - $errorType = E_USER_NOTICE; - - $logger = (new WPDeskLoggerFactory())->createWPDeskLogger(self::LOGGER_NAME); - $logger->pushHandler($this->prepareListenHandleThatIsWaitingForMessage('E_USER_NOTICE: ' . $someMessage)); - $this->expectException(Notice::class); - trigger_error($someMessage, $errorType); + if (WooCommerceCapture::isSupportedWCVersion()) { + (new WPDeskLoggerFactory())->createWPDeskLogger(); + $this->assertInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), "Logger should be captured."); + } else { + $this->markTestSkipped("Cant capture logger in WC < 3.5"); + } } +// +// This test won't work in PHPUnit 5.x. Enable when support for PHP 5.6 is dropped +// +// public function testOurLogsGetAllErrors() +// { +// $someMessage = 'whatever'; +// $errorType = E_USER_NOTICE; +// +// $logger = (new WPDeskLoggerFactory())->createWPDeskLogger(self::LOGGER_NAME); +// $logger->pushHandler($this->prepareListenHandleThatIsWaitingForMessage('E_USER_NOTICE: ' . $someMessage)); +// $this->expectException(Notice::class); +// trigger_error($someMessage, $errorType); +// } /** * Prepares listener that check if logger gets sent message @@ -75,11 +82,15 @@ class TestWPDeskLoggerFactory extends WP_UnitTestCase public function testOurLogsGetAllMessagesLoggedToWC() { - $someMessage = 'whatever'; - $logger = (new WPDeskLoggerFactory())->createWPDeskLogger(self::LOGGER_NAME); - $logger->pushHandler($this->prepareListenHandleThatIsWaitingForMessage($someMessage)); - - wc_get_logger()->debug($someMessage); + if (WooCommerceCapture::isSupportedWCVersion()) { + $someMessage = 'whatever'; + $logger = (new WPDeskLoggerFactory())->createWPDeskLogger(self::LOGGER_NAME); + $logger->pushHandler($this->prepareListenHandleThatIsWaitingForMessage($someMessage)); + + wc_get_logger()->debug($someMessage); + } else { + $this->markTestSkipped("Cant capture logger in WC < 3.5"); + } } public function testLoggerWorksAndCanLogInGeneral() @@ -108,20 +119,25 @@ class TestWPDeskLoggerFactory extends WP_UnitTestCase public function testAllLoggedMessagesAreWrittenToWC() { - $mockWcLogger = $this->createMock(WC_Logger::class); - $mockWcLogger - ->expects($this->atLeastOnce()) - ->method('log') - ->willReturn(true); + if (WooCommerceCapture::isSupportedWCVersion()) { + $mockWcLogger = $this->createMock(WC_Logger::class); + $mockWcLogger + ->expects($this->atLeastOnce()) + ->method('log') + ->willReturn(true); - add_filter('woocommerce_logging_class', function () use ($mockWcLogger) { - return $mockWcLogger; - }, 0, 100); + add_filter('woocommerce_logging_class', function () use ($mockWcLogger) { + return $mockWcLogger; + }, 0, 100); - $someMessage = 'whatever'; - $logger = (new WPDeskLoggerFactory())->createWPDeskLogger(self::LOGGER_NAME); + $someMessage = 'whatever'; + $logger = (new WPDeskLoggerFactory())->createWPDeskLogger(self::LOGGER_NAME); + + $logger->debug($someMessage); + } else { + $this->markTestSkipped("Cant capture logger in WC < 3.5 so we cant mock for this test"); + } - $logger->debug($someMessage); } public function testDisable() { diff --git a/tests/integration/WC/TestWooCommerceCapture.php b/tests/integration/WC/TestWooCommerceCapture.php index 2ce089b5252460e81be5fd85ed429324fa72b05b..2a687a277d632b53028a73bcf79dd225213c8d46 100644 --- a/tests/integration/WC/TestWooCommerceCapture.php +++ b/tests/integration/WC/TestWooCommerceCapture.php @@ -9,33 +9,45 @@ class TestWooCommerceCapture extends WP_UnitTestCase { public function testIfCanCaptureWcLogger() { - $wcCapture = new WooCommerceCapture($this->createMock(Logger::class)); - $wcCapture->captureWcLogger(); - - $this->assertInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), "Logger should be captured."); + if (WooCommerceCapture::isSupportedWCVersion()) { + $wcCapture = new WooCommerceCapture($this->createMock(Logger::class)); + $wcCapture->captureWcLogger(); + + $this->assertInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), "Logger should be captured."); + } else { + $this->markTestSkipped("Cant capture logger in WC < 3.5"); + } } public function testIfCanFreeWcLogger() { - $wcCapture = new WooCommerceCapture($this->createMock(Logger::class)); - $wcCapture->captureWcLogger(); - $wcCapture->freeWcLogger(); - $this->assertNotInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), - "Logger should be restored to original"); - - $wcCapture->captureWcLogger(); - $this->assertInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), "Logger should be captured."); - $wcCapture->freeWcLogger(); - $this->assertNotInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), - "Logger should be restored to original - twice"); + if (WooCommerceCapture::isSupportedWCVersion()) { + $wcCapture = new WooCommerceCapture($this->createMock(Logger::class)); + $wcCapture->captureWcLogger(); + $wcCapture->freeWcLogger(); + $this->assertNotInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), + "Logger should be restored to original"); + + $wcCapture->captureWcLogger(); + $this->assertInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), "Logger should be captured."); + $wcCapture->freeWcLogger(); + $this->assertNotInstanceOf(WooCommerceMonologPlugin::class, wc_get_logger(), + "Logger should be restored to original - twice"); + } else { + $this->markTestSkipped("Cant capture logger in WC < 3.5"); + } } public function testIfCantCaptureTwice() { - $this->expectException(WCLoggerAlreadyCaptured::class); - $wcCapture = new WooCommerceCapture($this->createMock(Logger::class)); - $wcCapture->captureWcLogger(); - $wcCapture->captureWcLogger(); + if (WooCommerceCapture::isSupportedWCVersion()) { + $this->expectException(WCLoggerAlreadyCaptured::class); + $wcCapture = new WooCommerceCapture($this->createMock(Logger::class)); + $wcCapture->captureWcLogger(); + $wcCapture->captureWcLogger(); + } else { + $this->markTestSkipped("Cant capture logger in WC < 3.5"); + } } }