Skip to content

Replacing Zend_Cache with symfony/cache component #40058

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: 2.4-develop
Choose a base branch
from

Conversation

jakwinkler
Copy link

@jakwinkler jakwinkler commented Jul 9, 2025

Description (*)

My biggest PR for Magento Open Source.
This PR removes Zend_Cache adapter and introduces symfony-cache to be used instead.

Manual testing scenarios (*)

  1. Do a lot of testing for cache cleaning
  2. Repeat and automate this testing

Contribution checklist (*)

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • README.md files for modified modules are updated and included in the pull request if any README.md predefined sections require an update
  • All automated tests passed successfully (all builds are green)

Resolved issues:

  1. resolves [Issue] Replacing Zend_Cache with symfony/cache component #40070: Replacing Zend_Cache with symfony/cache component

Copy link

m2-assistant bot commented Jul 9, 2025

Hi @jakwinkler. Thank you for your contribution!
Here are some useful tips on how you can test your changes using Magento test environment.
❗ Automated tests can be triggered manually with an appropriate comment:

  • @magento run all tests - run or re-run all required tests against the PR changes
  • @magento run <test-build(s)> - run or re-run specific test build(s)
    For example: @magento run Unit Tests

<test-build(s)> is a comma-separated list of build names.

Allowed build names are:
  1. Database Compare
  2. Functional Tests CE
  3. Functional Tests EE
  4. Functional Tests B2B
  5. Integration Tests
  6. Magento Health Index
  7. Sample Data Tests CE
  8. Sample Data Tests EE
  9. Sample Data Tests B2B
  10. Static Tests
  11. Unit Tests
  12. WebAPI Tests
  13. Semantic Version Checker

You can find more information about the builds here
ℹ️ Run only required test builds during development. Run all test builds before sending your pull request for review.


For more details, review the Code Contributions documentation.
Join Magento Community Engineering Slack and ask your questions in #github channel.

@jakwinkler
Copy link
Author

@ihor-sviziev check it out ;-) something we can merge before 2.5? ;-)

@ihor-sviziev
Copy link
Contributor

not sure. @engcom-Delta could someone from internal team review and decide whether we can merge it to 2.4 or not?

@jakwinkler
Copy link
Author

@magento run all tests

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR migrates Magento's cache system from Zend_Cache to Symfony Cache components, addressing a significant architectural change in the framework's caching infrastructure. The changes introduce Symfony Cache as the new caching backend while maintaining compatibility with existing Magento cache interfaces.

Key changes include:

  • Introduction of a new Symfony Cache adapter that implements Magento's FrontendInterface
  • Replacement of Zend Cache constants with new constants in FrontendInterface
  • Updates to Core class to use PSR-6 CacheItemPoolInterface instead of Zend_Cache_Core
  • Migration of cache backend factory to create Symfony adapters

Reviewed Changes

Copilot reviewed 27 out of 28 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
composer.json Updates dependencies to replace magento/zend-cache with symfony/cache
lib/internal/Magento/Framework/Cache/FrontendInterface.php Adds cache cleaning mode constants to replace Zend Cache constants
lib/internal/Magento/Framework/Cache/Frontend/Adapter/Symfony.php New Symfony Cache adapter implementing FrontendInterface
lib/internal/Magento/Framework/Cache/Core.php Complete rewrite to use PSR-6 CacheItemPoolInterface
lib/internal/Magento/Framework/App/Cache/Frontend/Factory.php Factory updated to create Symfony Cache adapters
Multiple test files Updated to use new constants and mock PSR interfaces

public function test($identifier)
{
$item = $this->cache->getItem($this->cleanIdentifier($identifier));
return $item->isHit() ? ($item->getMetadata()['mtime'] ?? time()) : false;
Copy link
Preview

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getMetadata() method is not part of the PSR-6 CacheItemInterface standard. This will cause a fatal error when used with standard PSR-6 implementations. Consider using a different approach to track modification time or document that this requires a specific cache adapter implementation.

Suggested change
return $item->isHit() ? ($item->getMetadata()['mtime'] ?? time()) : false;
if ($item->isHit()) {
$data = $item->get();
return $data['mtime'] ?? time();
}
return false;

Copilot uses AI. Check for mistakes.

public function test($identifier)
{
$item = $this->cachePool->getItem($this->_id($identifier));
return $item->isHit() ? ($item->getMetadata()['mtime'] ?? time()) : false;
Copy link
Preview

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getMetadata() method is not part of the PSR-6 CacheItemInterface standard. This will cause a fatal error when used with standard PSR-6 implementations. Consider using a different approach to track modification time or document that this requires a specific cache adapter implementation.

Suggested change
return $item->isHit() ? ($item->getMetadata()['mtime'] ?? time()) : false;
return $item->isHit() ? ($item->get()['mtime'] ?? time()) : false;

Copilot uses AI. Check for mistakes.

Copy link
Contributor

@ihor-sviziev ihor-sviziev Jul 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakwinkler I see all these issues related to PSR-6 are caused by using wrong interface for the cachePool. I guess the right interface should be \Symfony\Component\Cache\Adapter\AdapterInterface, which extends Psr\Cache\CacheItemPoolInterface

*/
public function remove($identifier)
{
return $this->cachePool->delete($this->_id($identifier));
Copy link
Preview

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The delete() method is not part of the PSR-6 CacheItemPoolInterface. The correct method name is deleteItem(). This will cause a fatal error.

Suggested change
return $this->cachePool->delete($this->_id($identifier));
return $this->cachePool->deleteItem($this->_id($identifier));

Copilot uses AI. Check for mistakes.

Comment on lines +234 to +244
$metadata = $item->getMetadata();
return [
'expire' => $metadata['expiry'] ?? 0,
'mtime' => $metadata['mtime'] ?? time(),
'tags' => [], // Tags are not directly supported by PSR-6 metadata
];
}
return false;
}

return;
/**
Copy link
Preview

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getMetadata() method used on line 234 is not part of the PSR-6 CacheItemInterface standard. This will cause a fatal error when used with standard PSR-6 implementations.

Suggested change
$metadata = $item->getMetadata();
return [
'expire' => $metadata['expiry'] ?? 0,
'mtime' => $metadata['mtime'] ?? time(),
'tags' => [], // Tags are not directly supported by PSR-6 metadata
];
}
return false;
}
return;
/**
// Retrieve metadata from a custom storage mechanism
$metadata = $this->retrieveMetadata($cacheId);
return [
'expire' => $metadata['expiry'] ?? 0,
'mtime' => $metadata['mtime'] ?? time(),
'tags' => $metadata['tags'] ?? [], // Tags retrieved from custom storage
];
}
return false;
}
/**
* Retrieve metadata for a given cache ID from custom storage.
*
* @param string $cacheId
* @return array
*/
private function retrieveMetadata($cacheId)
{
// Example implementation: Replace with actual storage logic
$metadataStorage = $this->getMetadataStorage();
return $metadataStorage[$cacheId] ?? [];
}
/**
* Get the metadata storage.
*
* @return array
*/
private function getMetadataStorage()
{
// Example implementation: Replace with actual storage logic
return [
'exampleCacheId' => [
'expiry' => time() + 3600,
'mtime' => time(),
'tags' => ['tag1', 'tag2'],
],
];
}
/**

Copilot uses AI. Check for mistakes.

@@ -455,9 +455,9 @@ public static function removeCache()
public static function clearCache($tag = null): void
{
if ($tag) {
self::$cache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, $tag);
self::$cache->clean(\Magento\Framework\Cache\FrontendInterface::CLEANING_MODE_MATCHING_TAG, (array)$tag);
Copy link
Preview

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The clean() method is being called on self::$cache which is typed as FrontendInterface, but the parameter casting to (array) suggests the original code expected a single string. This change in behavior could cause issues if calling code passes a string instead of an array.

Suggested change
self::$cache->clean(\Magento\Framework\Cache\FrontendInterface::CLEANING_MODE_MATCHING_TAG, (array)$tag);
$tags = is_array($tag) ? $tag : [$tag];
self::$cache->clean(\Magento\Framework\Cache\FrontendInterface::CLEANING_MODE_MATCHING_TAG, $tags);

Copilot uses AI. Check for mistakes.

$frontendFactory = function () use ($adaptee) {
return $adaptee;
};
$lowLevelFrontend = new Zend($frontendFactory);
$lowLevelFrontend = new Symfony($frontendFactory);
Copy link
Preview

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable $frontendFactory is a closure that returns a CacheItemPoolInterface mock, but the Symfony constructor expects a CacheItemPoolInterface directly, not a factory function. This will cause a type error.

Suggested change
$lowLevelFrontend = new Symfony($frontendFactory);
$cacheItemPool = $frontendFactory();
$lowLevelFrontend = new Symfony($cacheItemPool);

Copilot uses AI. Check for mistakes.

break;
// Symfony Cache does not have a direct SQLite adapter. PdoAdapter can be used with SQLite DSN.
// For now, fallback to FilesystemAdapter or throw an error.
throw new \Exception('SQLite cache backend is not directly supported by Symfony Cache adapters.');
Copy link
Preview

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using the generic Exception class instead of a more specific exception type makes error handling less precise. Consider using a more specific exception class like UnsupportedOperationException or creating a custom exception.

Suggested change
throw new \Exception('SQLite cache backend is not directly supported by Symfony Cache adapters.');
throw new \LogicException('SQLite cache backend is not directly supported by Symfony Cache adapters.');

Copilot uses AI. Check for mistakes.

break;
// Symfony Cache does not have an eAccelerator adapter.
// For now, fallback to FilesystemAdapter or throw an error.
throw new \Exception('eAccelerator cache backend is not directly supported by Symfony Cache adapters.');
Copy link
Preview

Copilot AI Jul 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Using the generic Exception class instead of a more specific exception type makes error handling less precise. Consider using a more specific exception class like UnsupportedOperationException or creating a custom exception.

Suggested change
throw new \Exception('eAccelerator cache backend is not directly supported by Symfony Cache adapters.');
throw new \LogicException('eAccelerator cache backend is not directly supported by Symfony Cache adapters.');

Copilot uses AI. Check for mistakes.

"magento/zend-db": "^1.16",
"magento/zend-pdf": "^1.16",
"monolog/monolog": "^3.6",
"opensearch-project/opensearch-php": "^2.3",
"pelago/emogrifier": "^7.0",
"php-amqplib/php-amqplib": "^3.2",
"phpseclib/mcrypt_compat": "^2.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was removed by misteke, it's not relates to this PR

@ihor-sviziev
Copy link
Contributor

@jakwinkler, please review the comments from Copilot and let us know if you would like anything to be fixed based on them. If no, I'll review the pull request

@jakwinkler
Copy link
Author

@ihor-sviziev I'm not entire sure about PSR-6 starndard with Magento, can you elaborate on that?

@ihor-sviziev
Copy link
Contributor

ihor-sviziev commented Jul 10, 2025

@magento run all tests

BTW, I checked - the bin/magento cache:flush command fails with the error:

bin/magento cache:flush

There is an error in /var/www/html/lib/internal/Magento/Framework/App/Cache/Manager.php at line: 109
Call to undefined method Symfony\Component\Cache\Adapter\TagAwareAdapter::clean()#0 /var/www/html/app/code/Magento/Backend/Console/Command/CacheFlushCommand.php(39): Magento\Framework\App\Cache\Manager->flush(Array)
#1 /var/www/html/app/code/Magento/Backend/Console/Command/AbstractCacheTypeManageCommand.php(64): Magento\Backend\Console\Command\CacheFlushCommand->performAction(Array)
#2 /var/www/html/vendor/symfony/console/Command/Command.php(326): Magento\Backend\Console\Command\AbstractCacheTypeManageCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#3 /var/www/html/lib/internal/Magento/Framework/Interception/Interceptor.php(58): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#4 /var/www/html/lib/internal/Magento/Framework/Interception/Interceptor.php(138): Magento\Backend\Console\Command\CacheFlushCommand\Interceptor->___callParent('run', Array)
#5 /var/www/html/lib/internal/Magento/Framework/Interception/Interceptor.php(153): Magento\Backend\Console\Command\CacheFlushCommand\Interceptor->Magento\Framework\Interception\{closure}(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#6 /var/www/html/generated/code/Magento/Backend/Console/Command/CacheFlushCommand/Interceptor.php(77): Magento\Backend\Console\Command\CacheFlushCommand\Interceptor->___callPlugins('run', Array, Array)
#7 /var/www/html/vendor/symfony/console/Application.php(1078): Magento\Backend\Console\Command\CacheFlushCommand\Interceptor->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 /var/www/html/vendor/symfony/console/Application.php(324): Symfony\Component\Console\Application->doRunCommand(Object(Magento\Backend\Console\Command\CacheFlushCommand\Interceptor), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#9 /var/www/html/lib/internal/Magento/Framework/Console/Cli.php(123): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 /var/www/html/vendor/symfony/console/Application.php(175): Magento\Framework\Console\Cli->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#11 /var/www/html/bin/magento(23): Symfony\Component\Console\Application->run()
#12 {main}

Looks like you've restricted pushing by maintainers, so I can't add the fix

@jakwinkler
Copy link
Author

@ihor-sviziev thanks for checking, I'll get back on this in 2-3 days max.

@engcom-Charlie
Copy link
Contributor

@magento create issue

Copy link
Contributor

@ihor-sviziev ihor-sviziev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please review the comments from Copilot and let us know if you would like anything to be fixed based on them.
Also, please fix the issue described here #40058 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Priority: P3 May be fixed according to the position in the backlog. Progress: pending review Progress: review Project: Community Picked PRs upvoted by the community Triage: Dev.Experience Issue related to Developer Experience and needs help with Triage to Confirm or Reject it
Projects
Status: Pending Review
Development

Successfully merging this pull request may close these issues.

[Issue] Replacing Zend_Cache with symfony/cache component
4 participants