diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 6cdce10d19..b7668ebeaf 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -33,6 +33,8 @@ jobs: TESTS_ZEND_CACHE_LIBMEMCACHED_HOST: 127.0.0.1 TESTS_ZEND_CACHE_LIBMEMCACHED_PORT: 11211 + TESTS_ZEND_CACHE_APC_ENABLED: true + # https://hub.docker.com/r/bitnami/openldap LDAP_ROOT: "dc=example,dc=com" LDAP_ALLOW_ANON_BINDING: false @@ -112,15 +114,15 @@ jobs: #bare for PHP >=7.2 - php-extensions-bare: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer, mbstring" #full for PHP <= 8.0 - - php-extensions-full: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer, mbstring, ctype, openssl, curl, gd, posix, pdo_sqlite, pdo_mysql, fileinfo, zip, sqlite, soap, bcmath, igbinary, bz2, lzf, memcached, memcache, ldap, sqlite, mcrypt, rar" + - php-extensions-full: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer, mbstring, apcu, ctype, openssl, curl, gd, posix, pdo_sqlite, pdo_mysql, fileinfo, zip, sqlite, soap, bcmath, igbinary, bz2, lzf, memcached, memcache, ldap, sqlite, mcrypt, rar" - php-version: "7.1" php-extensions-bare: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer" - php-version: "8.1" - php-extensions-full: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer, mbstring, ctype, openssl, curl, gd, posix, pdo_sqlite, pdo_mysql, fileinfo, zip, sqlite, soap, bcmath, igbinary, bz2, lzf, memcached, memcache, ldap, sqlite, mcrypt" + php-extensions-full: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer, mbstring, apcu, ctype, openssl, curl, gd, posix, pdo_sqlite, pdo_mysql, fileinfo, zip, sqlite, soap, bcmath, igbinary, bz2, lzf, memcached, memcache, ldap, sqlite, mcrypt" - php-version: "8.2" - php-extensions-full: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer, mbstring, ctype, openssl, curl, gd, posix, pdo_sqlite, pdo_mysql, fileinfo, zip, sqlite, soap, bcmath, igbinary, bz2, lzf, memcached, memcache, ldap, sqlite, mcrypt" + php-extensions-full: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer, mbstring, apcu, ctype, openssl, curl, gd, posix, pdo_sqlite, pdo_mysql, fileinfo, zip, sqlite, soap, bcmath, igbinary, bz2, lzf, memcached, memcache, ldap, sqlite, mcrypt" - php-version: "8.3" - php-extensions-full: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer, mbstring, ctype, openssl, curl, gd, posix, pdo_sqlite, pdo_mysql, fileinfo, zip, sqlite, soap, bcmath, igbinary, bz2, lzf, memcached, memcache, ldap, sqlite" + php-extensions-full: "none, iconv, json, libxml, xml, dom, simplexml, xmlwriter, tokenizer, mbstring, apcu, ctype, openssl, curl, gd, posix, pdo_sqlite, pdo_mysql, fileinfo, zip, sqlite, soap, bcmath, igbinary, bz2, lzf, memcached, memcache, ldap, sqlite" experimental: true steps: @@ -159,7 +161,7 @@ jobs: restore-keys: ${{ runner.os }}-${{ matrix.php-version }}-composer- - name: Install dependencies - run: composer install --prefer-dist --no-progress --no-suggest + run: composer install --prefer-dist --no-progress --no-interaction - name: Lint PHP source files run: | @@ -181,7 +183,7 @@ jobs: php-version: ${{ matrix.php-version }} tools: cs2pr extensions: ${{ matrix.php-extensions-full }} - ini-values: ${{ env.PHP_INI_VALUES }} + ini-values: ${{ env.PHP_INI_VALUES }}, apc.enable_cli=1 env: # https://github.com/shivammathur/setup-php/issues/407#issuecomment-773675741 fail-fast: true diff --git a/library/Zend/Cache/Backend/Apc.php b/library/Zend/Cache/Backend/Apc.php index 4f70b44b3c..5d34ab35d3 100644 --- a/library/Zend/Cache/Backend/Apc.php +++ b/library/Zend/Cache/Backend/Apc.php @@ -74,7 +74,7 @@ public function load($id, $doNotTestCacheValidity = false) { $tmp = apcu_fetch($id); if (is_array($tmp)) { - return $tmp[0]; + return $tmp[0] ?? false; } return false; } @@ -89,7 +89,7 @@ public function test($id) { $tmp = apcu_fetch($id); if (is_array($tmp)) { - return $tmp[1]; + return $tmp[1] ?? false; } return false; } diff --git a/library/Zend/Cache/Backend/File.php b/library/Zend/Cache/Backend/File.php index 38c8944ef2..551877c015 100644 --- a/library/Zend/Cache/Backend/File.php +++ b/library/Zend/Cache/Backend/File.php @@ -250,6 +250,7 @@ public function save($data, $id, $tags = [], $specificLifetime = false) $this->_recursiveMkdirAndChmod($id); } if (!is_writable($path)) { + $this->_log('Zend_Cache_Backend_File::save() : path ' . $path . ' is not writable'); return false; } } @@ -1011,14 +1012,20 @@ protected function _filePutContents($file, $string) $result = false; $f = @fopen($file, 'ab+'); if ($f) { - if ($this->_options['file_locking']) @flock($f, LOCK_EX); + if ($this->_options['file_locking']) { + @flock($f, LOCK_EX); + } fseek($f, 0); ftruncate($f, 0); $tmp = @fwrite($f, $string); if (!($tmp === FALSE)) { $result = true; + } else { + $this->_log("Zend_Cache_Backend_File::_filePutContents() : failed to write contents"); } @fclose($f); + } else { + $this->_log("Zend_Cache_Backend_File::_filePutContents() : we can't obtain handle"); } @chmod($file, $this->_options['cache_file_perm']); return $result; diff --git a/library/Zend/Cache/Backend/TwoLevels.php b/library/Zend/Cache/Backend/TwoLevels.php index 9cb04094b2..5353ea6214 100644 --- a/library/Zend/Cache/Backend/TwoLevels.php +++ b/library/Zend/Cache/Backend/TwoLevels.php @@ -195,23 +195,32 @@ public function test($id) */ public function save($data, $id, $tags = [], $specificLifetime = false, $priority = 8) { + $this->_log('Calling two level save', Zend_Log::DEBUG); $usage = $this->_getFastFillingPercentage('saving'); + $this->_log('Fast filling usage percentage on save: (' . gettype($usage) . ') ' . var_export($usage, true), Zend_Log::DEBUG); $boolFast = true; $lifetime = $this->getLifetime($specificLifetime); $preparedData = $this->_prepareData($data, $lifetime, $priority); if (($priority > 0) && (10 * $priority >= $usage)) { + $this->_log('Saving in fast and slow cache', Zend_Log::DEBUG); $fastLifetime = $this->_getFastLifetime($lifetime, $priority); $boolFast = $this->_fastBackend->save($preparedData, $id, [], $fastLifetime); + $this->_log($boolFast ? 'Saving in fast cache is OK' : 'Saving in fast cache is not OK', Zend_Log::DEBUG); $boolSlow = $this->_slowBackend->save($preparedData, $id, $tags, $lifetime); + $this->_log($boolSlow ? 'Saving in slow cache is OK' : 'Saving in slow cache is not OK', Zend_Log::DEBUG); } else { + $this->_log('Saving in slow cache only', Zend_Log::DEBUG); $boolSlow = $this->_slowBackend->save($preparedData, $id, $tags, $lifetime); + $this->_log($boolSlow ? 'Saving in slow cache is OK' : 'Saving in slow cache is not OK', Zend_Log::DEBUG); if ($boolSlow === true) { + $this->_log('Purging from fast cache on save', Zend_Log::DEBUG); $boolFast = $this->_fastBackend->remove($id); if (!$boolFast && !$this->_fastBackend->test($id)) { // some backends return false on remove() even if the key never existed. (and it won't if fast is full) // all we care about is that the key doesn't exist now $boolFast = true; } + $this->_log($boolFast ? 'Successfully purged from fast cache on save' : 'Failed to purge from fast cache on save', Zend_Log::DEBUG); } } @@ -229,18 +238,36 @@ public function save($data, $id, $tags = [], $specificLifetime = false, $priorit */ public function load($id, $doNotTestCacheValidity = false) { + $this->_log('Calling two level load', Zend_Log::DEBUG); $resultFast = $this->_fastBackend->load($id, $doNotTestCacheValidity); - if ($resultFast === false) { + $array = null; + $isFastResult = is_string($resultFast); + if ($isFastResult) { + $array = unserialize($resultFast, ['allowed_classes' => false]); + } + if (!is_array($array)) { + if ($isFastResult) { + // If the fast result data is not an array, it means it got corrupted somehow at rest. + // Remove it before trying the slow one. + $this->_fastBackend->remove($id); + } $resultSlow = $this->_slowBackend->load($id, $doNotTestCacheValidity); - if ($resultSlow === false) { + if (!is_string($resultSlow)) { // there is no cache at all for this id return false; } + $array = unserialize($resultSlow, ['allowed_classes' => false]); + if (!is_array($array)) { + // If the slow result data is not an array, it means it got corrupted somehow at rest. + // Remove it and return false. + $this->_slowBackend->remove($id); + return false; + } + $isFastResult = false; } - $array = $resultFast !== false ? unserialize($resultFast) : unserialize($resultSlow); - //In case no cache entry was found in the FastCache and auto-filling is enabled, copy data to FastCache - if ($resultFast === false && $this->_options['auto_fill_fast_cache']) { + //In case no cache entry was found in the FastCache (or was corrupted) and auto-filling is enabled, copy data to FastCache + if (!$isFastResult && $this->_options['auto_fill_fast_cache']) { $preparedData = $this->_prepareData($array['data'], $array['lifetime'], $array['priority']); $this->_fastBackend->save($preparedData, $id, [], $array['lifetime']); } @@ -461,7 +488,7 @@ public function getCapabilities() } /** - * Prepare a serialized array to store datas and metadatas informations + * Prepare a serialized array to store data and metadata information * * @param string $data data to store * @param int $lifetime original lifetime @@ -524,7 +551,7 @@ public function ___expire($id) private function _getFastFillingPercentage($mode) { - if ($mode == 'saving') { + if ($mode === 'saving') { // mode saving if ($this->_fastBackendFillingPercentage === null) { $this->_fastBackendFillingPercentage = $this->_fastBackend->getFillingPercentage(); diff --git a/tests/Zend/Cache/AllTests.php b/tests/Zend/Cache/AllTests.php index c7d419b2ef..b132a33dcd 100644 --- a/tests/Zend/Cache/AllTests.php +++ b/tests/Zend/Cache/AllTests.php @@ -104,9 +104,9 @@ public static function suite() $skipTest = new Zend_Cache_ApcBackendTest_SkipTests(); $skipTest->message = 'Tests are not enabled in TestConfiguration.php'; $suite->addTest($skipTest); - } elseif (!extension_loaded('apc')) { + } elseif (!extension_loaded('apcu')) { $skipTest = new Zend_Cache_ApcBackendTest_SkipTests(); - $skipTest->message = "Extension 'APC' is not loaded"; + $skipTest->message = "Extension 'apcu' is not loaded"; $suite->addTest($skipTest); } else { $suite->addTestSuite('Zend_Cache_ApcBackendTest'); @@ -218,9 +218,9 @@ public static function suite() $skipTest = new Zend_Cache_TwoLevelsBackendTest_SkipTests(); $skipTest->message = 'Tests are not enabled in TestConfiguration.php'; $suite->addTest($skipTest); - } elseif (!extension_loaded('apc')) { + } elseif (!extension_loaded('apcu')) { $skipTest = new Zend_Cache_TwoLevelsBackendTest_SkipTests(); - $skipTest->message = "Extension 'APC' is not loaded"; + $skipTest->message = "Extension 'apcu' is not loaded"; $suite->addTest($skipTest); } else { $suite->addTestSuite('Zend_Cache_TwoLevelsBackendTest'); diff --git a/tests/Zend/Cache/ApcBackendTest.php b/tests/Zend/Cache/ApcBackendTest.php index c1e66d6ba7..f4cef0c261 100644 --- a/tests/Zend/Cache/ApcBackendTest.php +++ b/tests/Zend/Cache/ApcBackendTest.php @@ -175,9 +175,6 @@ public function testGetTags() { } - /** - * @doesNotPerformAssertions - */ public function testSaveCorrectCall() { $this->_instance->setDirectives(['logging' => false]); @@ -185,9 +182,6 @@ public function testSaveCorrectCall() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithNullLifeTime() { $this->_instance->setDirectives(['logging' => false]); @@ -195,9 +189,6 @@ public function testSaveWithNullLifeTime() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithSpecificLifeTime() { $this->_instance->setDirectives(['logging' => false]); @@ -205,9 +196,6 @@ public function testSaveWithSpecificLifeTime() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testGetMetadatas($notag = true) { parent::testGetMetadatas($notag); diff --git a/tests/Zend/Cache/TwoLevelsBackendTest.php b/tests/Zend/Cache/TwoLevelsBackendTest.php index 70cc1d30cd..bf1bdbea43 100644 --- a/tests/Zend/Cache/TwoLevelsBackendTest.php +++ b/tests/Zend/Cache/TwoLevelsBackendTest.php @@ -99,30 +99,45 @@ public function testConstructorCorrectCall() public function testSaveOverwritesIfFastIsFull() { $slowBackend = 'File'; - $fastBackend = $this->createMock('Zend_Cache_Backend_Apc'); - $fastBackend->expects($this->at(0)) - ->method('getFillingPercentage') - ->will($this->returnValue(0)); - $fastBackend->expects($this->at(1)) + $fastBackend = $this->createPartialMock('Zend_Cache_Backend_Apc', ['getFillingPercentage']); + $fastBackend->expects($this->exactly(2)) ->method('getFillingPercentage') - ->will($this->returnValue(90)); - + ->willReturn(0, 90); $slowBackendOptions = [ - 'cache_dir' => $this->_cache_dir + 'cache_dir' => $this->_cache_dir, ]; + + $logStream = fopen('php://temp/maxmemory:4194304', 'a+b'); + $logger = new Zend_Log(new Zend_Log_Writer_Stream($logStream)); $cache = new Zend_Cache_Backend_TwoLevels([ 'fast_backend' => $fastBackend, 'slow_backend' => $slowBackend, 'slow_backend_options' => $slowBackendOptions, 'stats_update_factor' => 1 ]); + $cache->setDirectives(['logging' => true, 'logger' => $logger]); $id = 'test' . uniqid(); - $this->assertTrue($cache->save(10, $id)); //fast usage at 0% - - $this->assertTrue($cache->save(100, $id)); //fast usage at 90% - $this->assertEquals(100, $cache->load($id)); + + $saveResult = $cache->save(10, $id); + $failMessage = 'Failed to save when fast usage is 0. Two level logs: ' . "\n" . + stream_get_contents($logStream, -1, 0); + $this->assertTrue($saveResult, $failMessage); //fast usage at 0% + + $logger->debug('Finished saving when usage is at 0'); + + $saveResult = $cache->save(100, $id); + $failMessage = 'Failed to save when fast usage is 90. Two level logs: ' . "\n" . + stream_get_contents($logStream, -1, 0); + $this->assertTrue($saveResult, $failMessage); //fast usage at 90% + + $logger->debug('Finished saving when usage is at 90'); + + $loadResult = $cache->load($id); + $failMessage = 'Failed to load when fast usage is 90. Two level logs: ' . "\n" . + stream_get_contents($logStream, -1, 0); + $this->assertEquals(100, $loadResult, $failMessage); } /** diff --git a/tests/Zend/Cache/WinCacheBackendTest.php b/tests/Zend/Cache/WinCacheBackendTest.php index 15fcf0868a..d9b9ea243e 100644 --- a/tests/Zend/Cache/WinCacheBackendTest.php +++ b/tests/Zend/Cache/WinCacheBackendTest.php @@ -198,9 +198,6 @@ public function testGetTags() $this->markTestSkipped('This test skipped due to limitations in this adapter.'); } - /** - * @doesNotPerformAssertions - */ public function testSaveCorrectCall() { $this->_instance->setDirectives(['logging' => false]); @@ -208,9 +205,6 @@ public function testSaveCorrectCall() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithNullLifeTime() { $this->_instance->setDirectives(['logging' => false]); @@ -218,9 +212,6 @@ public function testSaveWithNullLifeTime() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithSpecificLifeTime() { $this->_instance->setDirectives(['logging' => false]); @@ -228,9 +219,6 @@ public function testSaveWithSpecificLifeTime() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testGetMetadatas($notag = true) { parent::testGetMetadatas($notag); diff --git a/tests/Zend/Cache/XcacheBackendTest.php b/tests/Zend/Cache/XcacheBackendTest.php index 0147128e6a..69be8afae7 100644 --- a/tests/Zend/Cache/XcacheBackendTest.php +++ b/tests/Zend/Cache/XcacheBackendTest.php @@ -129,9 +129,7 @@ public function testCleanModeNotMatchingTags2() public function testCleanModeNotMatchingTags3() { } - /** - * @doesNotPerformAssertions - */ + public function testSaveCorrectCall() { $this->_instance->setDirectives(['logging' => false]); @@ -139,9 +137,6 @@ public function testSaveCorrectCall() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithNullLifeTime() { $this->_instance->setDirectives(['logging' => false]); @@ -149,9 +144,6 @@ public function testSaveWithNullLifeTime() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithSpecificLifeTime() { $this->_instance->setDirectives(['logging' => false]); diff --git a/tests/Zend/Cache/ZendServerDiskTest.php b/tests/Zend/Cache/ZendServerDiskTest.php index 5d1e26cd43..809eaccc14 100755 --- a/tests/Zend/Cache/ZendServerDiskTest.php +++ b/tests/Zend/Cache/ZendServerDiskTest.php @@ -126,9 +126,7 @@ public function testCleanModeNotMatchingTags2() public function testCleanModeNotMatchingTags3() { } - /** - * @doesNotPerformAssertions - */ + public function testSaveCorrectCall() { $this->_instance->setDirectives(['logging' => false]); @@ -136,9 +134,6 @@ public function testSaveCorrectCall() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithNullLifeTime() { $this->_instance->setDirectives(['logging' => false]); @@ -146,9 +141,6 @@ public function testSaveWithNullLifeTime() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithSpecificLifeTime() { $this->_instance->setDirectives(['logging' => false]); diff --git a/tests/Zend/Cache/ZendServerShMemTest.php b/tests/Zend/Cache/ZendServerShMemTest.php index 4da810fc77..ac532a2ac3 100755 --- a/tests/Zend/Cache/ZendServerShMemTest.php +++ b/tests/Zend/Cache/ZendServerShMemTest.php @@ -126,9 +126,7 @@ public function testCleanModeNotMatchingTags2() public function testCleanModeNotMatchingTags3() { } - /** - * @doesNotPerformAssertions - */ + public function testSaveCorrectCall() { $this->_instance->setDirectives(['logging' => false]); @@ -136,9 +134,6 @@ public function testSaveCorrectCall() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithNullLifeTime() { $this->_instance->setDirectives(['logging' => false]); @@ -146,9 +141,6 @@ public function testSaveWithNullLifeTime() $this->_instance->setDirectives(['logging' => true]); } - /** - * @doesNotPerformAssertions - */ public function testSaveWithSpecificLifeTime() { $this->_instance->setDirectives(['logging' => false]);