diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php index a42ba1dd55202..419e4968dce6b 100644 --- a/app/code/Magento/Catalog/Block/Product/View.php +++ b/app/code/Magento/Catalog/Block/Product/View.php @@ -6,6 +6,7 @@ namespace Magento\Catalog\Block\Product; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Product; /** @@ -55,7 +56,7 @@ class View extends AbstractProduct implements \Magento\Framework\DataObject\Iden * @var \Magento\Customer\Model\Session */ protected $customerSession; - + /** * @var ProductRepositoryInterface */ @@ -371,7 +372,7 @@ public function getIdentities() $identities = $this->getProduct()->getIdentities(); $category = $this->_coreRegistry->registry('current_category'); if ($category) { - $identities[] = Product::CACHE_PRODUCT_CATEGORY_TAG . '_' . $category->getId(); + $identities[] = Category::CACHE_TAG . '_' . $category->getId(); } return $identities; } diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php index 4a28a1fff8906..23691ede5a3c4 100644 --- a/app/code/Magento/Catalog/Model/Product.php +++ b/app/code/Magento/Catalog/Model/Product.php @@ -2269,7 +2269,8 @@ public function getIdentities() $identities[] = self::CACHE_PRODUCT_CATEGORY_TAG . '_' . $categoryId; } } - if ($this->getOrigData('status') != $this->getData('status')) { + + if (($this->getOrigData('status') != $this->getData('status')) || $this->isStockStatusChanged()) { foreach ($this->getCategoryIds() as $categoryId) { $identities[] = self::CACHE_PRODUCT_CATEGORY_TAG . '_' . $categoryId; } @@ -2277,9 +2278,31 @@ public function getIdentities() if ($this->_appState->getAreaCode() == \Magento\Framework\App\Area::AREA_FRONTEND) { $identities[] = self::CACHE_TAG; } + return array_unique($identities); } + /** + * Check whether stock status changed + * + * @return bool + */ + private function isStockStatusChanged() + { + $stockItem = null; + $extendedAttributes = $this->getExtensionAttributes(); + if ($extendedAttributes !== null) { + $stockItem = $extendedAttributes->getStockItem(); + } + $stockData = $this->getStockData(); + return ( + (is_array($stockData)) + && array_key_exists('is_in_stock', $stockData) + && (null !== $stockItem) + && ($stockItem->getIsInStock() != $stockData['is_in_stock']) + ); + } + /** * Reload PriceInfo object * diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php index 95b546c012856..062300d67b9d1 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php @@ -84,6 +84,6 @@ public function testGetIdentities() ] ) ); - $this->assertEquals(['catalog_product_1', 'catalog_category_product_1'], $this->view->getIdentities()); + $this->assertEquals(['catalog_product_1', 'catalog_category_1'], $this->view->getIdentities()); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php index 1305271083482..ad637d347fc40 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php @@ -10,6 +10,7 @@ use Magento\Catalog\Model\Product; use Magento\Framework\Api\Data\ImageContentInterface; +use Magento\Framework\Api\ExtensibleDataInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Catalog\Model\Product\Attribute\Source\Status as Status; @@ -658,6 +659,20 @@ public function testGetIdentities($expected, $origData, $data, $isDeleted = fals */ public function getIdentitiesProvider() { + $extensionAttributesMock = $this->getMockBuilder(\Magento\Framework\Api\ExtensionAttributesInterface::class) + ->disableOriginalConstructor() + ->setMethods(['getStockItem']) + ->getMock(); + $stockItemMock = $this->getMockBuilder(\Magento\CatalogInventory\Api\Data\StockItemInterface::class) + ->disableOriginalConstructor() + ->getMock(); + $extensionAttributesMock->expects($this->any()) + ->method('getStockItem') + ->willReturn($stockItemMock); + $stockItemMock->expects($this->any()) + ->method('getIsInStock') + ->willReturn(true); + return [ 'no changes' => [ ['catalog_product_1'], @@ -708,7 +723,53 @@ public function getIdentitiesProvider() [0 => 'catalog_product_1'], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], - ] + ], + 'no stock status changes' => [ + [0 => 'catalog_product_1'], + ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], + [ + 'id' => 1, + 'name' => 'value', + 'category_ids' => [1], + 'status' => 1, + 'stock_data' => ['is_in_stock' => true], + ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY => $extensionAttributesMock, + ], + ], + 'no stock status data 1' => [ + [0 => 'catalog_product_1'], + ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], + [ + 'id' => 1, + 'name' => 'value', + 'category_ids' => [1], + 'status' => 1, + ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY => $extensionAttributesMock, + ], + ], + 'no stock status data 2' => [ + [0 => 'catalog_product_1'], + ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], + [ + 'id' => 1, + 'name' => 'value', + 'category_ids' => [1], + 'status' => 1, + 'stock_data' => ['is_in_stock' => true], + ], + ], + 'stock status changes' => [ + [0 => 'catalog_product_1', 1 => 'catalog_category_product_1'], + ['id' => 1, 'name' => 'value', 'category_ids' => [1], 'status' => 1], + [ + 'id' => 1, + 'name' => 'value', + 'category_ids' => [1], + 'status' => 1, + 'stock_data' => ['is_in_stock' => false], + ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY => $extensionAttributesMock, + ], + ], ]; } diff --git a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php index 9ac6a65410664..cabbfb807070c 100644 --- a/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php +++ b/app/code/Magento/CatalogInventory/Model/Indexer/Stock/AbstractAction.php @@ -9,6 +9,7 @@ namespace Magento\CatalogInventory\Model\Indexer\Stock; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; use Magento\Framework\App\ResourceConnection; /** @@ -65,7 +66,6 @@ abstract class AbstractAction */ private $eventManager; - /** * @param ResourceConnection $resource * @param \Magento\CatalogInventory\Model\ResourceModel\Indexer\StockFactory $indexerFactory @@ -247,17 +247,10 @@ protected function _reindexRows($productIds = []) $indexer->reindexEntity($byType[$indexer->getTypeId()]); } } - - $select = $connection->select() - ->distinct(true) - ->from($this->_getTable('catalog_category_product'), ['category_id']) - ->where('product_id IN(?)', $processIds); - - $affectedCategories = $connection->fetchCol($select); - $this->cacheContext->registerEntities(Category::CACHE_TAG, $affectedCategories); - + + $this->cacheContext->registerEntities(Product::CACHE_TAG, $productIds); $this->eventManager->dispatch('clean_cache_by_tags', ['object' => $this->cacheContext]); - + return $this; } diff --git a/app/code/Magento/Deploy/Model/Filesystem.php b/app/code/Magento/Deploy/Model/Filesystem.php index 44477d46ffbce..6555267766f5d 100644 --- a/app/code/Magento/Deploy/Model/Filesystem.php +++ b/app/code/Magento/Deploy/Model/Filesystem.php @@ -138,10 +138,11 @@ public function regenerateStatic( DirectoryList::TMP_MATERIALIZATION_DIR ] ); - // Trigger static assets compilation and deployment - $this->deployStaticContent($output); + // Trigger code generation $this->compile($output); + // Trigger static assets compilation and deployment + $this->deployStaticContent($output); } /** diff --git a/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php b/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php index 7a728d43aae01..9268fedd320be 100644 --- a/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php +++ b/app/code/Magento/Deploy/Test/Unit/Model/FilesystemTest.php @@ -153,23 +153,23 @@ public function testRegenerateStatic() $setupDiCompileCmd = $this->cmdPrefix . 'setup:di:compile'; $this->shellMock->expects($this->at(0)) ->method('execute') - ->with($staticContentDeployCmd); + ->with($setupDiCompileCmd); $this->shellMock->expects($this->at(1)) ->method('execute') - ->with($setupDiCompileCmd); + ->with($staticContentDeployCmd); $this->outputMock->expects($this->at(0)) ->method('writeln') - ->with('Starting deployment of static content'); + ->with('Starting compilation'); $this->outputMock->expects($this->at(2)) ->method('writeln') - ->with('Deployment of static content complete'); + ->with('Compilation complete'); $this->outputMock->expects($this->at(3)) ->method('writeln') - ->with('Starting compilation'); + ->with('Starting deployment of static content'); $this->outputMock->expects($this->at(5)) ->method('writeln') - ->with('Compilation complete'); + ->with('Deployment of static content complete'); $this->filesystem->regenerateStatic($this->outputMock); } diff --git a/app/code/Magento/Tax/Model/Plugin/OrderSave.php b/app/code/Magento/Tax/Model/Plugin/OrderSave.php index c64717e4d4744..d467627d7ff1b 100644 --- a/app/code/Magento/Tax/Model/Plugin/OrderSave.php +++ b/app/code/Magento/Tax/Model/Plugin/OrderSave.php @@ -112,66 +112,69 @@ protected function saveOrderTax(\Magento\Sales\Api\Data\OrderInterface $order) foreach ($taxes as $row) { $id = $row['id']; - foreach ($row['rates'] as $tax) { - if ($row['percent'] == null) { - $baseRealAmount = $row['base_amount']; - } else { - if ($row['percent'] == 0 || $tax['percent'] == 0) { - continue; + // @todo: should be refactored as part of MAGETWO-53366 + if (isset($row['rates'])) { + foreach ($row['rates'] as $tax) { + if ($row['percent'] == null) { + $baseRealAmount = $row['base_amount']; + } else { + if ($row['percent'] == 0 || $tax['percent'] == 0) { + continue; + } + $baseRealAmount = $row['base_amount'] / $row['percent'] * $tax['percent']; } - $baseRealAmount = $row['base_amount'] / $row['percent'] * $tax['percent']; - } - $hidden = isset($row['hidden']) ? $row['hidden'] : 0; - $priority = isset($tax['priority']) ? $tax['priority'] : 0; - $position = isset($tax['position']) ? $tax['position'] : 0; - $process = isset($row['process']) ? $row['process'] : 0; - $data = [ - 'order_id' => $order->getEntityId(), - 'code' => $tax['code'], - 'title' => $tax['title'], - 'hidden' => $hidden, - 'percent' => $tax['percent'], - 'priority' => $priority, - 'position' => $position, - 'amount' => $row['amount'], - 'base_amount' => $row['base_amount'], - 'process' => $process, - 'base_real_amount' => $baseRealAmount, - ]; + $hidden = isset($row['hidden']) ? $row['hidden'] : 0; + $priority = isset($tax['priority']) ? $tax['priority'] : 0; + $position = isset($tax['position']) ? $tax['position'] : 0; + $process = isset($row['process']) ? $row['process'] : 0; + $data = [ + 'order_id' => $order->getEntityId(), + 'code' => $tax['code'], + 'title' => $tax['title'], + 'hidden' => $hidden, + 'percent' => $tax['percent'], + 'priority' => $priority, + 'position' => $position, + 'amount' => $row['amount'], + 'base_amount' => $row['base_amount'], + 'process' => $process, + 'base_real_amount' => $baseRealAmount, + ]; - /** @var $orderTax \Magento\Tax\Model\Sales\Order\Tax */ - $orderTax = $this->orderTaxFactory->create(); - $result = $orderTax->setData($data)->save(); + /** @var $orderTax \Magento\Tax\Model\Sales\Order\Tax */ + $orderTax = $this->orderTaxFactory->create(); + $result = $orderTax->setData($data)->save(); - if (isset($ratesIdQuoteItemId[$id])) { - foreach ($ratesIdQuoteItemId[$id] as $quoteItemId) { - if ($quoteItemId['code'] == $tax['code']) { - $itemId = null; - $associatedItemId = null; - if (isset($quoteItemId['id'])) { - //This is a product item - $item = $order->getItemByQuoteItemId($quoteItemId['id']); - $itemId = $item->getId(); - } elseif (isset($quoteItemId['associated_item_id'])) { - //This item is associated with a product item - $item = $order->getItemByQuoteItemId($quoteItemId['associated_item_id']); - $associatedItemId = $item->getId(); - } + if (isset($ratesIdQuoteItemId[$id])) { + foreach ($ratesIdQuoteItemId[$id] as $quoteItemId) { + if ($quoteItemId['code'] == $tax['code']) { + $itemId = null; + $associatedItemId = null; + if (isset($quoteItemId['id'])) { + //This is a product item + $item = $order->getItemByQuoteItemId($quoteItemId['id']); + $itemId = $item->getId(); + } elseif (isset($quoteItemId['associated_item_id'])) { + //This item is associated with a product item + $item = $order->getItemByQuoteItemId($quoteItemId['associated_item_id']); + $associatedItemId = $item->getId(); + } - $data = [ - 'item_id' => $itemId, - 'tax_id' => $result->getTaxId(), - 'tax_percent' => $quoteItemId['percent'], - 'associated_item_id' => $associatedItemId, - 'amount' => $quoteItemId['amount'], - 'base_amount' => $quoteItemId['base_amount'], - 'real_amount' => $quoteItemId['real_amount'], - 'real_base_amount' => $quoteItemId['real_base_amount'], - 'taxable_item_type' => $quoteItemId['item_type'], - ]; - /** @var $taxItem \Magento\Sales\Model\Order\Tax\Item */ - $taxItem = $this->taxItemFactory->create(); - $taxItem->setData($data)->save(); + $data = [ + 'item_id' => $itemId, + 'tax_id' => $result->getTaxId(), + 'tax_percent' => $quoteItemId['percent'], + 'associated_item_id' => $associatedItemId, + 'amount' => $quoteItemId['amount'], + 'base_amount' => $quoteItemId['base_amount'], + 'real_amount' => $quoteItemId['real_amount'], + 'real_base_amount' => $quoteItemId['real_base_amount'], + 'taxable_item_type' => $quoteItemId['item_type'], + ]; + /** @var $taxItem \Magento\Sales\Model\Order\Tax\Item */ + $taxItem = $this->taxItemFactory->create(); + $taxItem->setData($data)->save(); + } } } } diff --git a/composer.json b/composer.json index e03d701d3ef48..c71fb71ced98a 100644 --- a/composer.json +++ b/composer.json @@ -34,6 +34,8 @@ "magento/zendframework1": "1.12.16", "colinmollenhour/credis": "1.6", "colinmollenhour/php-redis-session-abstract": "1.1", + "colinmollenhour/cache-backend-redis": "1.9", + "colinmollenhour/cache-backend-file": "1.4", "composer/composer": "1.0.0-beta1", "monolog/monolog": "1.16.0", "oyejorge/less.php": "~1.7.0", @@ -188,17 +190,16 @@ "magento/language-zh_hans_cn": "100.1.0-rc2", "magento/framework": "100.1.0-rc2", "trentrichardson/jquery-timepicker-addon": "1.4.3", - "colinmollenhour/cache-backend-redis": "1.8", "components/jquery": "1.11.0", "blueimp/jquery-file-upload": "5.6.14", "components/jqueryui": "1.10.4", "twbs/bootstrap": "3.1.0", - "tinymce/tinymce": "3.4.7" + "tinymce/tinymce": "3.4.7", + "magento-hackathon/magento-composer-installer": "*" }, "extra": { "component_paths": { "trentrichardson/jquery-timepicker-addon": "lib/web/jquery/jquery-ui-timepicker-addon.js", - "colinmollenhour/cache-backend-redis": "lib/internal/Cm/Cache/Backend/Redis.php", "components/jquery": [ "lib/web/jquery.js", "lib/web/jquery/jquery.min.js", diff --git a/composer.lock b/composer.lock index c34b3a5440be1..6ca328e7f36cf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "2a5edf483d6b4e8b92814e135f0e4de3", - "content-hash": "9afe891ffda877cd8460a80a081f7c1d", + "hash": "03aa0fd88ec1218fac917ab6ddebe520", + "content-hash": "4289e5ed7fb777b66c153e0187458241", "packages": [ { "name": "braintree/braintree_php", @@ -54,6 +54,78 @@ "description": "Braintree PHP Client Library", "time": "2015-11-19 19:14:47" }, + { + "name": "colinmollenhour/cache-backend-file", + "version": "1.4", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/Cm_Cache_Backend_File.git", + "reference": "51251b80a817790eb624fbe2afc882c14f3c4fb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_File/zipball/51251b80a817790eb624fbe2afc882c14f3c4fb0", + "reference": "51251b80a817790eb624fbe2afc882c14f3c4fb0", + "shasum": "" + }, + "require": { + "magento-hackathon/magento-composer-installer": "*" + }, + "type": "magento-module", + "autoload": { + "classmap": [ + "File.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin Mollenhour" + } + ], + "description": "The stock Zend_Cache_Backend_File backend has extremely poor performance for cleaning by tags making it become unusable as the number of cached items increases. This backend makes many changes resulting in a huge performance boost, especially for tag cleaning.", + "homepage": "https://github.com/colinmollenhour/Cm_Cache_Backend_File", + "time": "2016-05-02 16:24:47" + }, + { + "name": "colinmollenhour/cache-backend-redis", + "version": "1.9", + "source": { + "type": "git", + "url": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis.git", + "reference": "6319714bb3a4fe699c5db0edb887f5e8fe40a6dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/colinmollenhour/Cm_Cache_Backend_Redis/zipball/6319714bb3a4fe699c5db0edb887f5e8fe40a6dc", + "reference": "6319714bb3a4fe699c5db0edb887f5e8fe40a6dc", + "shasum": "" + }, + "require": { + "magento-hackathon/magento-composer-installer": "*" + }, + "type": "magento-module", + "autoload": { + "classmap": [ + "Cm/Cache/Backend/Redis.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin Mollenhour" + } + ], + "description": "Zend_Cache backend using Redis with full support for tags.", + "homepage": "https://github.com/colinmollenhour/Cm_Cache_Backend_Redis", + "time": "2016-05-02 16:23:36" + }, { "name": "colinmollenhour/credis", "version": "1.6", diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php index 6b2f339f43d10..542a679cf08bf 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ComposerTest.php @@ -341,7 +341,11 @@ public function testComponentPathsInRoot() "The {$component} is specified in 'extra->component_paths', but missing in 'replace' section" ); } + $allowedMissingComponents = file(__DIR__ . '/_files/blacklist/missing_components.txt'); foreach (array_keys(self::$rootJson['replace']) as $replace) { + if (in_array($replace, $allowedMissingComponents)) { + continue; + } if (!MagentoComponent::matchMagentoComponent($replace)) { $this->assertArrayHasKey( $replace, diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/missing_components.txt b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/missing_components.txt new file mode 100644 index 0000000000000..76a0540cbd071 --- /dev/null +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/missing_components.txt @@ -0,0 +1 @@ +magento-hackathon/magento-composer-installer \ No newline at end of file diff --git a/lib/internal/Cm/Cache/Backend/File.php b/lib/internal/Cm/Cache/Backend/File.php deleted file mode 100644 index ed0707176bbe5..0000000000000 --- a/lib/internal/Cm/Cache/Backend/File.php +++ /dev/null @@ -1,716 +0,0 @@ - null, // Path to cache files - 'file_name_prefix' => 'cm', // Prefix for cache directories created - 'file_locking' => true, // Best to keep enabled - 'read_control' => false, // Use a checksum to detect corrupt data - 'read_control_type' => 'crc32', // If read_control is enabled, which checksum algorithm to use - 'hashed_directory_level' => 2, // How many characters should be used to create sub-directories - 'use_chmod' => FALSE, // Do not use chmod on files and directories (should use umask() to control permissions) - 'directory_mode' => 0770, // Filesystem permissions for created directories (requires use_chmod) - 'file_mode' => 0660, // Filesystem permissions for created files (requires use_chmod) - ); - - /** @var bool */ - protected $_isTagDirChecked; - - /** - * @param array $options - */ - public function __construct(array $options = array()) - { - // Backwards compatibility ZF 1.11 and ZF 1.12 - if (isset($options['hashed_directory_umask'])) { - $options['directory_mode'] = $options['hashed_directory_umask']; - } - if (isset($options['cache_file_umask'])) { - $options['file_mode'] = $options['cache_file_umask']; - } - - // Don't use parent constructor - while (list($name, $value) = each($options)) { - $this->setOption($name, $value); - } - - // Check cache dir - if ($this->_options['cache_dir'] !== null) { // particular case for this option - $this->setCacheDir($this->_options['cache_dir']); - } else { - $this->setCacheDir(self::getTmpDir() . DIRECTORY_SEPARATOR, false); - } - - // Validate prefix - if (isset($this->_options['file_name_prefix'])) { // particular case for this option - if (!preg_match('~^[a-zA-Z0-9_]+$~D', $this->_options['file_name_prefix'])) { - Zend_Cache::throwException('Invalid file_name_prefix : must use only [a-zA-Z0-9_]'); - } - } - - // See #ZF-4422 - if (is_string($this->_options['directory_mode'])) { - $this->_options['directory_mode'] = octdec($this->_options['directory_mode']); - } - if (is_string($this->_options['file_mode'])) { - $this->_options['file_mode'] = octdec($this->_options['file_mode']); - } - $this->_options['hashed_directory_umask'] = $this->_options['directory_mode']; - $this->_options['cache_file_umask'] = $this->_options['file_mode']; - } - - /** - * Test if a cache is available for the given id and (if yes) return it (false else) - * - * @param string $id cache id - * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested - * @return string|bool cached datas - */ - public function load($id, $doNotTestCacheValidity = false) - { - $file = $this->_file($id); - $cache = $this->_getCache($file, true); - if ( ! $cache) { - return false; - } - list($metadatas, $data) = $cache; - if ( ! $doNotTestCacheValidity && (time() > $metadatas['expire'])) { - // ?? $this->remove($id); - return false; - } - if ($this->_options['read_control']) { - $hashData = $this->_hash($data, $this->_options['read_control_type']); - $hashControl = $metadatas['hash']; - if ($hashData != $hashControl) { - // Problem detected by the read control ! - $this->_log('Zend_Cache_Backend_File::load() / read_control : stored hash and computed hash do not match'); - $this->remove($id); - return false; - } - } - return $data; - } - - /** - * Save some string datas into a cache record - * - * Note : $data is always "string" (serialization is done by the - * core not by the backend) - * - * @param string $data Datas to cache - * @param string $id Cache id - * @param array $tags Array of strings, the cache record will be tagged by each string entry - * @param bool|int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) - * @return boolean true if no problem - */ - public function save($data, $id, $tags = array(), $specificLifetime = false) - { - $file = $this->_file($id); - $path = $this->_path($id); - if ($this->_options['hashed_directory_level'] > 0) { - if (!is_writable($path)) { - // maybe, we just have to build the directory structure - $this->_recursiveMkdirAndChmod($id); - } - if (!is_writable($path)) { - return false; - } - } - if ($this->_options['read_control']) { - $hash = $this->_hash($data, $this->_options['read_control_type']); - } else { - $hash = ''; - } - $metadatas = array( - 'hash' => $hash, - 'mtime' => time(), - 'expire' => $this->_expireTime($this->getLifetime($specificLifetime)), - 'tags' => implode(',', $tags), - ); - $res = $this->_filePutContents($file, serialize($metadatas)."\n".$data); - $res = $res && $this->_updateIdsTags(array($id), $tags, 'merge'); - return $res; - } - - /** - * Remove a cache record - * - * @param string $id cache id - * @return boolean true if no problem - */ - public function remove($id) - { - $file = $this->_file($id); - $metadatas = $this->_getCache($file, false); - if ($metadatas) { - $boolRemove = $this->_remove($file); - $boolTags = $this->_updateIdsTags(array($id), explode(',', $metadatas['tags']), 'diff'); - return $boolRemove && $boolTags; - } - return false; - } - - /** - * Clean some cache records - * - * Available modes are : - * 'all' (default) => remove all cache entries ($tags is not used) - * 'old' => remove too old cache entries ($tags is not used) - * 'matchingTag' => remove cache entries matching all given tags - * ($tags can be an array of strings or a single string) - * 'notMatchingTag' => remove cache entries not matching one of the given tags - * ($tags can be an array of strings or a single string) - * 'matchingAnyTag' => remove cache entries matching any given tags - * ($tags can be an array of strings or a single string) - * - * @param string $mode - * @param array $tags - * @return boolean true if no problem - */ - public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) - { - // We use this protected method to hide the recursive stuff - clearstatcache(); - switch($mode) { - case Zend_Cache::CLEANING_MODE_ALL: - case Zend_Cache::CLEANING_MODE_OLD: - return $this->_clean($this->_options['cache_dir'], $mode); - default: - return $this->_cleanNew($mode, $tags); - } - } - - /** - * Return an array of stored tags - * - * @return array array of stored tags (string) - */ - public function getTags() - { - $prefix = $this->_tagFile(''); - $prefixLen = strlen($prefix); - $tags = array(); - foreach (@glob($prefix . '*') as $tagFile) { - $tags[] = substr($tagFile, $prefixLen); - } - return $tags; - } - - /** - * Return an array of stored cache ids which match given tags - * - * In case of multiple tags, a logical AND is made between tags - * - * @param array $tags array of tags - * @return array array of matching cache ids (string) - */ - public function getIdsMatchingTags($tags = array()) - { - return $this->_getIdsByTags(Zend_Cache::CLEANING_MODE_MATCHING_TAG, $tags); - } - - /** - * Return an array of stored cache ids which don't match given tags - * - * In case of multiple tags, a logical OR is made between tags - * - * @param array $tags array of tags - * @return array array of not matching cache ids (string) - */ - public function getIdsNotMatchingTags($tags = array()) - { - return $this->_getIdsByTags(Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG, $tags); - } - - /** - * Return an array of stored cache ids which match any given tags - * - * In case of multiple tags, a logical AND is made between tags - * - * @param array $tags array of tags - * @return array array of any matching cache ids (string) - */ - public function getIdsMatchingAnyTags($tags = array()) - { - return $this->_getIdsByTags(Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, $tags); - } - - /** - * Return an array of metadatas for the given cache id - * - * The array must include these keys : - * - expire : the expire timestamp - * - tags : a string array of tags - * - mtime : timestamp of last modification time - * - * @param string $id cache id - * @return array array of metadatas (false if the cache id is not found) - */ - public function getMetadatas($id) - { - $metadatas = $this->_getCache($this->_file($id), false); - if ($metadatas) { - $metadatas['tags'] = explode(',' ,$metadatas['tags']); - } - return $metadatas; - } - - /** - * Give (if possible) an extra lifetime to the given cache id - * - * @param string $id cache id - * @param int $extraLifetime - * @return boolean true if ok - */ - public function touch($id, $extraLifetime) - { - $file = $this->_file($id); - $cache = $this->_getCache($file, true); - if (!$cache) { - return false; - } - list($metadatas, $data) = $cache; - if (time() > $metadatas['expire']) { - return false; - } - $newMetadatas = array( - 'hash' => $metadatas['hash'], - 'mtime' => time(), - 'expire' => $metadatas['expire'] + $extraLifetime, - 'tags' => $metadatas['tags'] - ); - return !! $this->_filePutContents($file, serialize($newMetadatas)."\n".$data); - } - - /** - * Get a metadatas record and optionally the data as well - * - * @param string $file Cache file - * @param bool $withData - * @return array|bool - */ - protected function _getCache($file, $withData) - { - if (!is_file($file) || ! ($fd = @fopen($file, 'rb'))) { - return false; - } - if ($this->_options['file_locking']) flock($fd, LOCK_SH); - $metadata = fgets($fd); - if ( ! $metadata) { - if ($this->_options['file_locking']) flock($fd, LOCK_UN); - fclose($fd); - return false; - } - if ($withData) { - $data = stream_get_contents($fd); - } - if ($this->_options['file_locking']) flock($fd, LOCK_UN); - fclose($fd); - $metadata = @unserialize(rtrim($metadata,"\n")); - if ($withData) { - return array($metadata, $data); - } - return $metadata; - } - - /** - * Get meta data from a cache record - * - * @param string $id Cache id - * @return array|bool Associative array of meta data - */ - protected function _getMetadatas($id) - { - return $this->_getCache($this->_file($id), false); - } - - /** - * Set a metadatas record - * - * @param string $id Cache id - * @param array $metadatas Associative array of metadatas - * @param boolean $save optional pass false to disable saving to file - * @return boolean True if no problem - */ - protected function _setMetadatas($id, $metadatas, $save = true) - { - // TODO - implement for unit tests ___expire method - return true; - } - - /** - * Return the complete directory path of a filename (including hashedDirectoryStructure) - * - * Uses multiple letters for a single-level hash rather than multiple levels - * - * @param string $id Cache id - * @param boolean $parts if true, returns array of directory parts instead of single string - * @return string|array Complete directory path - */ - protected function _path($id, $parts = false) - { - $partsArray = array(); - $root = $this->_options['cache_dir']; - $prefix = $this->_options['file_name_prefix']; - if ($this->_options['hashed_directory_level']>0) { - $hash = hash('adler32', $id); - $root = $root . $prefix . '--' . substr($hash, -$this->_options['hashed_directory_level']) . DIRECTORY_SEPARATOR; - $partsArray[] = $root; - } - if ($parts) { - return $partsArray; - } else { - return $root; - } - } - - /** - * Clean some cache records (protected method used for recursive stuff) - * - * Available modes are : - * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) - * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) - * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags - * ($tags can be an array of strings or a single string) - * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} - * ($tags can be an array of strings or a single string) - * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags - * ($tags can be an array of strings or a single string) - * - * @param string $dir Directory to clean - * @param string $mode Clean mode - * @param array $tags - * @throws Zend_Cache_Exception - * @return boolean True if no problem - */ - protected function _clean($dir, $mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) - { - if (!is_dir($dir)) { - return false; - } - $result = true; - $glob = @glob($dir . $this->_options['file_name_prefix'] . '--*'); - if ($glob === false) { - return true; - } - foreach ($glob as $file) { - if (is_file($file)) { - if ($mode == Zend_Cache::CLEANING_MODE_ALL) { - $result = @unlink($file) && $result; - continue; - } - - $id = $this->_fileNameToId(basename($file)); - $_file = $this->_file($id); - if ($file != $_file) { - @unlink($file); - continue; - } - $metadatas = $this->_getCache($file, false); - if ( ! $metadatas) { - @unlink($file); - continue; - } - if ($mode == Zend_Cache::CLEANING_MODE_OLD) { - if (time() > $metadatas['expire']) { - $result = $this->_remove($file) && $result; - $result = $this->_updateIdsTags(array($id), explode(',', $metadatas['tags']), 'diff') && $result; - } - continue; - } else { - Zend_Cache::throwException('Invalid mode for clean() method.'); - } - } - if ((is_dir($file)) and ($this->_options['hashed_directory_level']>0)) { - // Recursive call - $result = $this->_clean($file . DIRECTORY_SEPARATOR, $mode) && $result; - if ($mode == 'all') { - // if mode=='all', we try to drop the structure too - @rmdir($file); - } - } - } - if ($mode == 'all') { - foreach (glob($this->_tagFile('*')) as $tagFile) { - @unlink($tagFile); - } - } - return $result; - } - - /** - * Clean some cache records (protected method used for recursive stuff) - * - * Available modes are : - * Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used) - * Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used) - * Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags - * ($tags can be an array of strings or a single string) - * Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags} - * ($tags can be an array of strings or a single string) - * Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags - * ($tags can be an array of strings or a single string) - * - * @param string $mode Clean mode - * @param array $tags Array of tags - * @throws Zend_Cache_Exception - * @return boolean True if no problem - */ - protected function _cleanNew($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) - { - $result = true; - $ids = $this->_getIdsByTags($mode, $tags); - foreach ($ids as $id) { - $idFile = $this->_file($id); - if (is_file($idFile)) { - $result = $result && $this->_remove($idFile); - } - } - switch($mode) - { - case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: - foreach ($tags as $tag) { - $tagFile = $this->_tagFile($tag); - if (is_file($tagFile)) { - $result = $result && $this->_remove($tagFile); - } - } - break; - case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: - case Zend_Cache::CLEANING_MODE_MATCHING_TAG: - $this->_updateIdsTags($ids, $tags, 'diff'); - break; - } - return $result; - } - - /** - * @param string $mode - * @param array $tags - * @return array - */ - protected function _getIdsByTags($mode, $tags) - { - $ids = array(); - switch($mode) { - case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: - $ids = $this->getIds(); - if ($tags) { - foreach ($tags as $tag) { - if ( ! $ids) { - break; // early termination optimization - } - $ids = array_diff($ids, $this->_getTagIds($tag)); - } - } - break; - case Zend_Cache::CLEANING_MODE_MATCHING_TAG: - if ($tags) { - $tag = array_shift($tags); - $ids = $this->_getTagIds($tag); - foreach ($tags as $tag) { - if ( ! $ids) { - break; // early termination optimization - } - $ids = array_intersect($ids, $this->_getTagIds($tag)); - } - $ids = array_unique($ids); - } - break; - case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: - foreach ($tags as $tag) { - $ids = array_merge($ids,$this->_getTagIds($tag)); - } - $ids = array_unique($ids); - break; - } - return $ids; - } - - /** - * Make and return a file name (with path) - * - * @param string $id Cache id - * @return string File name (with path) - */ - protected function _tagFile($id) - { - $path = $this->_tagPath(); - $fileName = $this->_idToFileName($id); - return $path . $fileName; - } - - /** - * Return the complete directory path where tags are stored - * - * @return string Complete directory path - */ - protected function _tagPath() - { - $path = $this->_options['cache_dir'] . DIRECTORY_SEPARATOR . $this->_options['file_name_prefix']. '-tags' . DIRECTORY_SEPARATOR; - if ( ! $this->_isTagDirChecked) { - if ( ! is_dir($path)) { - if (@mkdir($path, $this->_options['use_chmod'] ? $this->_options['directory_mode'] : 0777) && $this->_options['use_chmod']) { - @chmod($path, $this->_options['directory_mode']); // see #ZF-320 (this line is required in some configurations) - } - } - $this->_isTagDirChecked = true; - } - return $path; - } - - /** - * @param string|resource $tag - * @return array - */ - protected function _getTagIds($tag) - { - if (is_resource($tag)) { - $ids = stream_get_contents($tag); - } else { - $ids = @file_get_contents($this->_tagFile($tag)); - } - if( ! $ids) { - return array(); - } - $ids = trim(substr($ids, 0, strrpos($ids, "\n"))); - return $ids ? explode("\n", $ids) : array(); - } - - /** - * @param array $ids - * @param array $tags - * @param string $mode - * @return bool - */ - protected function _updateIdsTags($ids, $tags, $mode) - { - $result = true; - foreach($tags as $tag) { - $file = $this->_tagFile($tag); - if (file_exists($file)) { - /* - * Next code is commented because it's produce bug - * Bug about removing cache ids but in some case cache ids is empty, but related tags is removed - */ -// if ( ! $ids && $mode == 'diff') { -// $result = $this->_remove($file); -// } - if ($mode == 'diff' || (rand(1,100) == 1 && filesize($file) > 4096)) { - $file = $this->_tagFile($tag); - if ( ! ($fd = fopen($file, 'rb+'))) { - $result = false; - continue; - } - if ($this->_options['file_locking']) flock($fd, LOCK_EX); - if ($mode == 'diff') { - $_ids = array_diff($this->_getTagIds($fd), $ids); - } else { // if ($mode == 'merge') - $_ids = array_merge($this->_getTagIds($fd), $ids); - } - fseek($fd, 0); - ftruncate($fd, 0); - $result = fwrite($fd, implode("\n", array_unique($_ids))."\n") && $result; - if ($this->_options['file_locking']) flock($fd, LOCK_UN); - fclose($fd); - } - else { - $result = file_put_contents($file, implode("\n", $ids)."\n", FILE_APPEND | ($this->_options['file_locking'] ? LOCK_EX : 0)) && $result; - } - } else if ($mode == 'merge') { - $result = $this->_filePutContents($file, implode("\n", $ids)."\n") && $result; - } - } - return $result; - } - - /** - * Put the given string into the given file - * - * @param string $file File complete path - * @param string $string String to put in file - * @return boolean true if no problem - */ - protected function _filePutContents($file, $string) - { - $result = @file_put_contents($file, $string, $this->_options['file_locking'] ? LOCK_EX : 0); - if ($result && $this->_options['use_chmod']) { - @chmod($file, $this->_options['file_mode']); - } - return $result; - } - - /** - * Make the directory structure for the given id - * - * @param string $id cache id - * @return boolean true - */ - protected function _recursiveMkdirAndChmod($id) - { - if ($this->_options['hashed_directory_level'] <=0) { - return true; - } - $partsArray = $this->_path($id, true); - foreach ($partsArray as $part) { - if (!is_dir($part)) { - @mkdir($part, $this->_options['use_chmod'] ? $this->_options['directory_mode'] : 0777); - if ($this->_options['use_chmod']) { - @chmod($part, $this->_options['directory_mode']); // see #ZF-320 (this line is required in some configurations) - } - } - } - return true; - } - - /** - * For unit testing only - * @param $id - */ - public function ___expire($id) - { - $metadata = $this->_getMetadatas($id); - $this->touch($id, 1 - $metadata['expire']); - } - -} diff --git a/lib/internal/Cm/Cache/Backend/Redis.php b/lib/internal/Cm/Cache/Backend/Redis.php deleted file mode 100644 index 08349361c358b..0000000000000 --- a/lib/internal/Cm/Cache/Backend/Redis.php +++ /dev/null @@ -1,961 +0,0 @@ -_redis = new Credis_Client($options['server'], $port, $timeout, $persistent); - - if ( isset($options['force_standalone']) && $options['force_standalone']) { - $this->_redis->forceStandalone(); - } - - $connectRetries = isset($options['connect_retries']) ? (int)$options['connect_retries'] : self::DEFAULT_CONNECT_RETRIES; - $this->_redis->setMaxConnectRetries($connectRetries); - - if ( ! empty($options['read_timeout']) && $options['read_timeout'] > 0) { - $this->_redis->setReadTimeout((float) $options['read_timeout']); - } - - if ( ! empty($options['password'])) { - $this->_redis->auth($options['password']) or Zend_Cache::throwException('Unable to authenticate with the redis server.'); - } - - // Always select database on startup in case persistent connection is re-used by other code - if (empty($options['database'])) { - $options['database'] = 0; - } - $this->_redis->select( (int) $options['database']) or Zend_Cache::throwException('The redis database could not be selected.'); - - if ( isset($options['notMatchingTags']) ) { - $this->_notMatchingTags = (bool) $options['notMatchingTags']; - } - - if ( isset($options['compress_tags'])) { - $this->_compressTags = (int) $options['compress_tags']; - } - - if ( isset($options['compress_data'])) { - $this->_compressData = (int) $options['compress_data']; - } - - if ( isset($options['lifetimelimit'])) { - $this->_lifetimelimit = (int) min($options['lifetimelimit'], self::MAX_LIFETIME); - } - - if ( isset($options['compress_threshold'])) { - $this->_compressThreshold = (int) $options['compress_threshold']; - } - - if ( isset($options['automatic_cleaning_factor']) ) { - $this->_options['automatic_cleaning_factor'] = (int) $options['automatic_cleaning_factor']; - } else { - $this->_options['automatic_cleaning_factor'] = 0; - } - - if ( isset($options['compression_lib']) ) { - $this->_compressionLib = (string) $options['compression_lib']; - } - else if ( function_exists('snappy_compress') ) { - $this->_compressionLib = 'snappy'; - } - else if ( function_exists('lz4_compress')) { - $this->_compressionLib = 'l4z'; - } - else if ( function_exists('lzf_compress') ) { - $this->_compressionLib = 'lzf'; - } - else { - $this->_compressionLib = 'gzip'; - } - $this->_compressPrefix = substr($this->_compressionLib,0,2).self::COMPRESS_PREFIX; - - if ( isset($options['sunion_chunk_size']) && $options['sunion_chunk_size'] > 0) { - $this->_sunionChunkSize = (int) $options['sunion_chunk_size']; - } - - if (isset($options['use_lua'])) { - $this->_useLua = (bool) $options['use_lua']; - } - - if (isset($options['lua_max_c_stack'])) { - $this->_luaMaxCStack = (int) $options['lua_max_c_stack']; - } - } - - /** - * Load value with given id from cache - * - * @param string $id Cache id - * @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested - * @return bool|string - */ - public function load($id, $doNotTestCacheValidity = false) - { - $data = $this->_redis->hGet(self::PREFIX_KEY.$id, self::FIELD_DATA); - if ($data === NULL) { - return FALSE; - } - return $this->_decodeData($data); - } - - /** - * Test if a cache is available or not (for the given id) - * - * @param string $id Cache id - * @return bool|int False if record is not available or "last modified" timestamp of the available cache record - */ - public function test($id) - { - $mtime = $this->_redis->hGet(self::PREFIX_KEY.$id, self::FIELD_MTIME); - return ($mtime ? $mtime : FALSE); - } - - /** - * Save some string datas into a cache record - * - * Note : $data is always "string" (serialization is done by the - * core not by the backend) - * - * @param string $data Datas to cache - * @param string $id Cache id - * @param array $tags Array of strings, the cache record will be tagged by each string entry - * @param bool|int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime) - * @throws CredisException - * @return boolean True if no problem - */ - public function save($data, $id, $tags = array(), $specificLifetime = false) - { - if(!is_array($tags)) - $tags = $tags ? array($tags) : array(); - else - $tags = array_flip(array_flip($tags)); - - $lifetime = $this->getLifetime($specificLifetime); - - if ($this->_useLua) { - $sArgs = array( - self::PREFIX_KEY, - self::FIELD_DATA, - self::FIELD_TAGS, - self::FIELD_MTIME, - self::FIELD_INF, - self::SET_TAGS, - self::PREFIX_TAG_IDS, - self::SET_IDS, - $id, - $this->_encodeData($data, $this->_compressData), - $this->_encodeData(implode(',',$tags), $this->_compressTags), - time(), - $lifetime ? 0 : 1, - min($lifetime, self::MAX_LIFETIME), - $this->_notMatchingTags ? 1 : 0 - ); - - $res = $this->_redis->evalSha(self::LUA_SAVE_SH1, $tags, $sArgs); - if (is_null($res)) { - $script = - "local oldTags = redis.call('HGET', ARGV[1]..ARGV[9], ARGV[3]) ". - "redis.call('HMSET', ARGV[1]..ARGV[9], ARGV[2], ARGV[10], ARGV[3], ARGV[11], ARGV[4], ARGV[12], ARGV[5], ARGV[13]) ". - "if (ARGV[13] == '0') then ". - "redis.call('EXPIRE', ARGV[1]..ARGV[9], ARGV[14]) ". - "end ". - "if next(KEYS) ~= nil then ". - "redis.call('SADD', ARGV[6], unpack(KEYS)) ". - "for _, tagname in ipairs(KEYS) do ". - "redis.call('SADD', ARGV[7]..tagname, ARGV[9]) ". - "end ". - "end ". - "if (ARGV[15] == '1') then ". - "redis.call('SADD', ARGV[8], ARGV[9]) ". - "end ". - "if (oldTags ~= false) then ". - "return oldTags ". - "else ". - "return '' ". - "end"; - $res = $this->_redis->eval($script, $tags, $sArgs); - } - - // Process removed tags if cache entry already existed - if ($res) { - $oldTags = explode(',', $this->_decodeData($res)); - if ($remTags = ($oldTags ? array_diff($oldTags, $tags) : FALSE)) - { - // Update the id list for each tag - foreach($remTags as $tag) - { - $this->_redis->sRem(self::PREFIX_TAG_IDS . $tag, $id); - } - } - } - - return TRUE; - } - - // Get list of tags previously assigned - $oldTags = $this->_decodeData($this->_redis->hGet(self::PREFIX_KEY.$id, self::FIELD_TAGS)); - $oldTags = $oldTags ? explode(',', $oldTags) : array(); - - $this->_redis->pipeline()->multi(); - - // Set the data - $result = $this->_redis->hMSet(self::PREFIX_KEY.$id, array( - self::FIELD_DATA => $this->_encodeData($data, $this->_compressData), - self::FIELD_TAGS => $this->_encodeData(implode(',',$tags), $this->_compressTags), - self::FIELD_MTIME => time(), - self::FIELD_INF => $lifetime ? 0 : 1, - )); - if( ! $result) { - throw new CredisException("Could not set cache key $id"); - } - - // Set expiration if specified - if ($lifetime) { - $this->_redis->expire(self::PREFIX_KEY.$id, min($lifetime, self::MAX_LIFETIME)); - } - - // Process added tags - if ($tags) - { - // Update the list with all the tags - $this->_redis->sAdd( self::SET_TAGS, $tags); - - // Update the id list for each tag - foreach($tags as $tag) - { - $this->_redis->sAdd(self::PREFIX_TAG_IDS . $tag, $id); - } - } - - // Process removed tags - if ($remTags = ($oldTags ? array_diff($oldTags, $tags) : FALSE)) - { - // Update the id list for each tag - foreach($remTags as $tag) - { - $this->_redis->sRem(self::PREFIX_TAG_IDS . $tag, $id); - } - } - - // Update the list with all the ids - if($this->_notMatchingTags) { - $this->_redis->sAdd(self::SET_IDS, $id); - } - - $this->_redis->exec(); - - return TRUE; - } - - /** - * Remove a cache record - * - * @param string $id Cache id - * @return boolean True if no problem - */ - public function remove($id) - { - // Get list of tags for this id - $tags = explode(',', $this->_decodeData($this->_redis->hGet(self::PREFIX_KEY.$id, self::FIELD_TAGS))); - - $this->_redis->pipeline()->multi(); - - // Remove data - $this->_redis->del(self::PREFIX_KEY.$id); - - // Remove id from list of all ids - if($this->_notMatchingTags) { - $this->_redis->sRem( self::SET_IDS, $id ); - } - - // Update the id list for each tag - foreach($tags as $tag) { - $this->_redis->sRem(self::PREFIX_TAG_IDS . $tag, $id); - } - - $result = $this->_redis->exec(); - - return (bool) $result[0]; - } - - /** - * @param array $tags - */ - protected function _removeByNotMatchingTags($tags) - { - $ids = $this->getIdsNotMatchingTags($tags); - if($ids) - { - $this->_redis->pipeline()->multi(); - - // Remove data - $this->_redis->del( $this->_preprocessIds($ids)); - - // Remove ids from list of all ids - if($this->_notMatchingTags) { - $this->_redis->sRem( self::SET_IDS, $ids); - } - - $this->_redis->exec(); - } - } - - /** - * @param array $tags - */ - protected function _removeByMatchingTags($tags) - { - $ids = $this->getIdsMatchingTags($tags); - if($ids) - { - $this->_redis->pipeline()->multi(); - - // Remove data - $this->_redis->del( $this->_preprocessIds($ids)); - - // Remove ids from list of all ids - if($this->_notMatchingTags) { - $this->_redis->sRem( self::SET_IDS, $ids); - } - - $this->_redis->exec(); - } - } - - /** - * @param array $tags - */ - protected function _removeByMatchingAnyTags($tags) - { - if ($this->_useLua) { - $tags = array_chunk($tags, $this->_sunionChunkSize); - foreach ($tags as $chunk) { - $chunk = $this->_preprocessTagIds($chunk); - $args = array(self::PREFIX_KEY, self::SET_TAGS, self::SET_IDS, ($this->_notMatchingTags ? 1 : 0), (int) $this->_luaMaxCStack); - if ( ! $this->_redis->evalSha(self::LUA_CLEAN_SH1, $chunk, $args)) { - $script = - "for i = 1, #KEYS, ARGV[5] do ". - "local keysToDel = redis.call('SUNION', unpack(KEYS, i, math.min(#KEYS, i + ARGV[5] - 1))) ". - "for _, keyname in ipairs(keysToDel) do ". - "redis.call('DEL', ARGV[1]..keyname) ". - "if (ARGV[4] == '1') then ". - "redis.call('SREM', ARGV[3], keyname) ". - "end ". - "end ". - "redis.call('DEL', unpack(KEYS, i, math.min(#KEYS, i + ARGV[5] - 1))) ". - "redis.call('SREM', ARGV[2], unpack(KEYS, i, math.min(#KEYS, i + ARGV[5] - 1))) ". - "end ". - "return true"; - $this->_redis->eval($script, $chunk, $args); - } - } - return; - } - - $ids = $this->getIdsMatchingAnyTags($tags); - - $this->_redis->pipeline()->multi(); - - if($ids) - { - // Remove data - $this->_redis->del( $this->_preprocessIds($ids)); - - // Remove ids from list of all ids - if($this->_notMatchingTags) { - $this->_redis->sRem( self::SET_IDS, $ids); - } - } - - // Remove tag id lists - $this->_redis->del( $this->_preprocessTagIds($tags)); - - // Remove tags from list of tags - $this->_redis->sRem( self::SET_TAGS, $tags); - - $this->_redis->exec(); - } - - /** - * Clean up tag id lists since as keys expire the ids remain in the tag id lists - */ - protected function _collectGarbage() - { - // Clean up expired keys from tag id set and global id set - - if ($this->_useLua) { - $sArgs = array(self::PREFIX_KEY, self::SET_TAGS, self::SET_IDS, self::PREFIX_TAG_IDS, ($this->_notMatchingTags ? 1 : 0)); - $allTags = (array) $this->_redis->sMembers(self::SET_TAGS); - $tagsCount = count($allTags); - $counter = 0; - $tagsBatch = array(); - foreach ($allTags as $tag) { - $tagsBatch[] = $tag; - $counter++; - if (count($tagsBatch) == 10 || $counter == $tagsCount ) { - if ( ! $this->_redis->evalSha(self::LUA_GC_SH1, $tagsBatch, $sArgs)) { - $script = - "local tagKeys = {} ". - "local expired = {} ". - "local expiredCount = 0 ". - "local notExpiredCount = 0 ". - "for _, tagName in ipairs(KEYS) do ". - "tagKeys = redis.call('SMEMBERS', ARGV[4]..tagName) ". - "for __, keyName in ipairs(tagKeys) do ". - "if (redis.call('EXISTS', ARGV[1]..keyName) == 0) then ". - "expiredCount = expiredCount + 1 ". - "expired[expiredCount] = keyName ". - /* Redis Lua scripts have a hard limit of 8000 parameters per command */ - "if (expiredCount == 7990) then ". - "redis.call('SREM', ARGV[4]..tagName, unpack(expired)) ". - "if (ARGV[5] == '1') then ". - "redis.call('SREM', ARGV[3], unpack(expired)) ". - "end ". - "expiredCount = 0 ". - "expired = {} ". - "end ". - "else ". - "notExpiredCount = notExpiredCount + 1 ". - "end ". - "end ". - "if (expiredCount > 0) then ". - "redis.call('SREM', ARGV[4]..tagName, unpack(expired)) ". - "if (ARGV[5] == '1') then ". - "redis.call('SREM', ARGV[3], unpack(expired)) ". - "end ". - "end ". - "if (notExpiredCount == 0) then ". - "redis.call ('DEL', ARGV[4]..tagName) ". - "redis.call ('SREM', ARGV[2], tagName) ". - "end ". - "expired = {} ". - "expiredCount = 0 ". - "notExpiredCount = 0 ". - "end ". - "return true"; - $this->_redis->eval($script, $tagsBatch, $sArgs); - } - $tagsBatch = array(); - /* Give Redis some time to handle other requests */ - usleep(20000); - } - } - return; - } - - $exists = array(); - $tags = (array) $this->_redis->sMembers(self::SET_TAGS); - foreach($tags as $tag) - { - // Get list of expired ids for each tag - $tagMembers = $this->_redis->sMembers(self::PREFIX_TAG_IDS . $tag); - $numTagMembers = count($tagMembers); - $expired = array(); - $numExpired = $numNotExpired = 0; - if($numTagMembers) { - while ($id = array_pop($tagMembers)) { - if( ! isset($exists[$id])) { - $exists[$id] = $this->_redis->exists(self::PREFIX_KEY.$id); - } - if ($exists[$id]) { - $numNotExpired++; - } - else { - $numExpired++; - $expired[] = $id; - - // Remove incrementally to reduce memory usage - if (count($expired) % 100 == 0 && $numNotExpired > 0) { - $this->_redis->sRem( self::PREFIX_TAG_IDS . $tag, $expired); - if($this->_notMatchingTags) { // Clean up expired ids from ids set - $this->_redis->sRem( self::SET_IDS, $expired); - } - $expired = array(); - } - } - } - if( ! count($expired)) continue; - } - - // Remove empty tags or completely expired tags - if ($numExpired == $numTagMembers) { - $this->_redis->del(self::PREFIX_TAG_IDS . $tag); - $this->_redis->sRem(self::SET_TAGS, $tag); - } - // Clean up expired ids from tag ids set - else if (count($expired)) { - $this->_redis->sRem( self::PREFIX_TAG_IDS . $tag, $expired); - if($this->_notMatchingTags) { // Clean up expired ids from ids set - $this->_redis->sRem( self::SET_IDS, $expired); - } - } - unset($expired); - } - - // Clean up global list of ids for ids with no tag - if($this->_notMatchingTags) { - // TODO - } - } - - /** - * Clean some cache records - * - * Available modes are : - * 'all' (default) => remove all cache entries ($tags is not used) - * 'old' => runs _collectGarbage() - * 'matchingTag' => supported - * 'notMatchingTag' => supported - * 'matchingAnyTag' => supported - * - * @param string $mode Clean mode - * @param array $tags Array of tags - * @throws Zend_Cache_Exception - * @return boolean True if no problem - */ - public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array()) - { - if( $tags && ! is_array($tags)) { - $tags = array($tags); - } - - try { - if ($mode == Zend_Cache::CLEANING_MODE_ALL) { - return $this->_redis->flushDb(); - } - if ($mode == Zend_Cache::CLEANING_MODE_OLD) { - $this->_collectGarbage(); - return TRUE; - } - if ( ! count($tags)) { - return TRUE; - } - switch ($mode) - { - case Zend_Cache::CLEANING_MODE_MATCHING_TAG: - - $this->_removeByMatchingTags($tags); - break; - - case Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG: - - $this->_removeByNotMatchingTags($tags); - break; - - case Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG: - - $this->_removeByMatchingAnyTags($tags); - break; - - default: - Zend_Cache::throwException('Invalid mode for clean() method: '.$mode); - } - } catch (CredisException $e) { - Zend_Cache::throwException('Error cleaning cache by mode '.$mode.': '.$e->getMessage(), $e); - } - return TRUE; - } - - /** - * Return true if the automatic cleaning is available for the backend - * - * @return boolean - */ - public function isAutomaticCleaningAvailable() - { - return TRUE; - } - - /** - * Set the frontend directives - * - * @param array $directives Assoc of directives - * @throws Zend_Cache_Exception - * @return void - */ - public function setDirectives($directives) - { - parent::setDirectives($directives); - $lifetime = $this->getLifetime(false); - if ($lifetime > self::MAX_LIFETIME) { - Zend_Cache::throwException('Redis backend has a limit of 30 days (2592000 seconds) for the lifetime'); - } - } - - /** - * Return an array of stored cache ids - * - * @return array array of stored cache ids (string) - */ - public function getIds() - { - if($this->_notMatchingTags) { - return (array) $this->_redis->sMembers(self::SET_IDS); - } else { - $keys = $this->_redis->keys(self::PREFIX_KEY . '*'); - $prefixLen = strlen(self::PREFIX_KEY); - foreach($keys as $index => $key) { - $keys[$index] = substr($key, $prefixLen); - } - return $keys; - } - } - - /** - * Return an array of stored tags - * - * @return array array of stored tags (string) - */ - public function getTags() - { - return (array) $this->_redis->sMembers(self::SET_TAGS); - } - - /** - * Return an array of stored cache ids which match given tags - * - * In case of multiple tags, a logical AND is made between tags - * - * @param array $tags array of tags - * @return array array of matching cache ids (string) - */ - public function getIdsMatchingTags($tags = array()) - { - if ($tags) { - return (array) $this->_redis->sInter( $this->_preprocessTagIds($tags) ); - } - return array(); - } - - /** - * Return an array of stored cache ids which don't match given tags - * - * In case of multiple tags, a negated logical AND is made between tags - * - * @param array $tags array of tags - * @return array array of not matching cache ids (string) - */ - public function getIdsNotMatchingTags($tags = array()) - { - if( ! $this->_notMatchingTags) { - Zend_Cache::throwException("notMatchingTags is currently disabled."); - } - if ($tags) { - return (array) $this->_redis->sDiff( self::SET_IDS, $this->_preprocessTagIds($tags) ); - } - return (array) $this->_redis->sMembers( self::SET_IDS ); - } - - /** - * Return an array of stored cache ids which match any given tags - * - * In case of multiple tags, a logical OR is made between tags - * - * @param array $tags array of tags - * @return array array of any matching cache ids (string) - */ - public function getIdsMatchingAnyTags($tags = array()) - { - $result = array(); - if ($tags) { - $chunks = array_chunk($tags, $this->_sunionChunkSize); - foreach ($chunks as $chunk) { - $result = array_merge($result, (array) $this->_redis->sUnion( $this->_preprocessTagIds($chunk))); - } - if (count($chunks) > 1) { - $result = array_unique($result); // since we are chunking requests, we must de-duplicate member names - } - } - return $result; - } - - /** - * Return the filling percentage of the backend storage - * - * @throws Zend_Cache_Exception - * @return int integer between 0 and 100 - */ - public function getFillingPercentage() - { - $maxMem = $this->_redis->config('GET','maxmemory'); - if (0 == (int) $maxMem['maxmemory']) { - return 1; - } - $info = $this->_redis->info(); - return round( - ($info['used_memory']/$maxMem['maxmemory']*100) - ,0 - ,PHP_ROUND_HALF_UP - ); - } - - /** - * Return an array of metadatas for the given cache id - * - * The array must include these keys : - * - expire : the expire timestamp - * - tags : a string array of tags - * - mtime : timestamp of last modification time - * - * @param string $id cache id - * @return array array of metadatas (false if the cache id is not found) - */ - public function getMetadatas($id) - { - list($tags, $mtime, $inf) = $this->_redis->hMGet(self::PREFIX_KEY.$id, array(self::FIELD_TAGS, self::FIELD_MTIME, self::FIELD_INF)); - if( ! $mtime) { - return FALSE; - } - $tags = explode(',', $this->_decodeData($tags)); - $expire = $inf === '1' ? FALSE : time() + $this->_redis->ttl(self::PREFIX_KEY.$id); - - return array( - 'expire' => $expire, - 'tags' => $tags, - 'mtime' => $mtime, - ); - } - - /** - * Give (if possible) an extra lifetime to the given cache id - * - * @param string $id cache id - * @param int $extraLifetime - * @return boolean true if ok - */ - public function touch($id, $extraLifetime) - { - list($inf) = $this->_redis->hGet(self::PREFIX_KEY.$id, self::FIELD_INF); - if ($inf === '0') { - $expireAt = time() + $this->_redis->ttl(self::PREFIX_KEY.$id) + $extraLifetime; - return (bool) $this->_redis->expireAt(self::PREFIX_KEY.$id, $expireAt); - } - return false; - } - - /** - * Return an associative array of capabilities (booleans) of the backend - * - * The array must include these keys : - * - automatic_cleaning (is automating cleaning necessary) - * - tags (are tags supported) - * - expired_read (is it possible to read expired cache records - * (for doNotTestCacheValidity option for example)) - * - priority does the backend deal with priority when saving - * - infinite_lifetime (is infinite lifetime can work with this backend) - * - get_list (is it possible to get the list of cache ids and the complete list of tags) - * - * @return array associative of with capabilities - */ - public function getCapabilities() - { - return array( - 'automatic_cleaning' => ($this->_options['automatic_cleaning_factor'] > 0), - 'tags' => true, - 'expired_read' => false, - 'priority' => false, - 'infinite_lifetime' => true, - 'get_list' => true, - ); - } - - /** - * @param string $data - * @param int $level - * @throws CredisException - * @return string - */ - protected function _encodeData($data, $level) - { - if ($this->_compressionLib && $level && strlen($data) >= $this->_compressThreshold) { - switch($this->_compressionLib) { - case 'snappy': $data = snappy_compress($data); break; - case 'lzf': $data = lzf_compress($data); break; - case 'l4z': $data = lz4_compress($data,($level > 1 ? true : false)); break; - case 'gzip': $data = gzcompress($data, $level); break; - default: throw new CredisException("Unrecognized 'compression_lib'."); - } - if( ! $data) { - throw new CredisException("Could not compress cache data."); - } - return $this->_compressPrefix.$data; - } - return $data; - } - - /** - * @param bool|string $data - * @return string - */ - protected function _decodeData($data) - { - if (substr($data,2,3) == self::COMPRESS_PREFIX) { - switch(substr($data,0,2)) { - case 'sn': return snappy_uncompress(substr($data,5)); - case 'lz': return lzf_decompress(substr($data,5)); - case 'l4': return lz4_uncompress(substr($data,5)); - case 'gz': case 'zc': return gzuncompress(substr($data,5)); - } - } - return $data; - } - - /** - * @param $item - * @param $index - * @param $prefix - */ - protected function _preprocess(&$item, $index, $prefix) - { - $item = $prefix . $item; - } - - /** - * @param $ids - * @return array - */ - protected function _preprocessIds($ids) - { - array_walk($ids, array($this, '_preprocess'), self::PREFIX_KEY); - return $ids; - } - - /** - * @param $tags - * @return array - */ - protected function _preprocessTagIds($tags) - { - array_walk($tags, array($this, '_preprocess'), self::PREFIX_TAG_IDS); - return $tags; - } - - /** - * Required to pass unit tests - * - * @param string $id - * @return void - */ - public function ___expire($id) - { - $this->_redis->del(self::PREFIX_KEY.$id); - } - -} diff --git a/lib/internal/Magento/Framework/Api/DataObjectHelper.php b/lib/internal/Magento/Framework/Api/DataObjectHelper.php index 8243568ae9275..1dacf2e25440c 100644 --- a/lib/internal/Magento/Framework/Api/DataObjectHelper.php +++ b/lib/internal/Magento/Framework/Api/DataObjectHelper.php @@ -182,20 +182,32 @@ protected function setComplexValue( = 'get' . \Magento\Framework\Api\SimpleDataObjectConverter::snakeCaseToUpperCamelCase( $extensionAttributeKey ); - $extensionAttributeType = $this->methodsMapProcessor->getMethodReturnType( + $methodReturnType = $this->methodsMapProcessor->getMethodReturnType( $returnType, $extensionAttributeGetterMethodName ); - if ($this->typeProcessor->isArrayType($extensionAttributeType)) { - $extensionAttributeType = $this->typeProcessor->getArrayItemType($extensionAttributeType); - } - if (!$this->typeProcessor->isTypeSimple($extensionAttributeType)) { - $value[$extensionAttributeKey] = $this->objectFactory->create( - $extensionAttributeType, - ['data' => $extensionAttributeValue] - ); - } else { + $extensionAttributeType = $this->typeProcessor->isArrayType($methodReturnType) + ? $this->typeProcessor->getArrayItemType($methodReturnType) + : $methodReturnType; + if ($this->typeProcessor->isTypeSimple($extensionAttributeType)) { $value[$extensionAttributeKey] = $extensionAttributeValue; + } else { + if ($this->typeProcessor->isArrayType($methodReturnType)) { + foreach ($extensionAttributeValue as $key => $extensionAttributeArrayValue) { + $extensionAttribute = $this->objectFactory->create($extensionAttributeType, []); + $this->populateWithArray( + $extensionAttribute, + $extensionAttributeArrayValue, + $extensionAttributeType + ); + $value[$extensionAttributeKey][$key] = $extensionAttribute; + } + } else { + $value[$extensionAttributeKey] = $this->objectFactory->create( + $extensionAttributeType, + ['data' => $extensionAttributeValue] + ); + } } } $object = $this->extensionFactory->create(get_class($dataObject), ['data' => $value]); diff --git a/lib/internal/Magento/Framework/App/PageCache/Identifier.php b/lib/internal/Magento/Framework/App/PageCache/Identifier.php index 0c95ded21bce4..8ec9452b8cbf3 100644 --- a/lib/internal/Magento/Framework/App/PageCache/Identifier.php +++ b/lib/internal/Magento/Framework/App/PageCache/Identifier.php @@ -41,7 +41,7 @@ public function getValue() { $data = [ $this->request->isSecure(), - $this->request->getRequestUri(), + $this->request->getUriString(), $this->request->get(\Magento\Framework\App\Response\Http::COOKIE_VARY_STRING) ?: $this->context->getVaryString() ]; diff --git a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/IdentifierTest.php b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/IdentifierTest.php index c6fbe72a76367..8c1b6de95d304 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/PageCache/IdentifierTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/PageCache/IdentifierTest.php @@ -5,94 +5,114 @@ */ namespace Magento\Framework\App\Test\Unit\PageCache; +use Magento\Framework\App\Http\Context; +use Magento\Framework\App\PageCache\Identifier; +use Magento\Framework\App\Response\Http; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; class IdentifierTest extends \PHPUnit_Framework_TestCase { - /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager - */ + /** Test value for cache vary string */ + const VARY = '123'; + + /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ private $objectManager; + /** @var Context */ + private $contextMock; + + /** @var HttpRequest */ + private $requestMock; + + /** @var Identifier */ + private $model; + /** * @return \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ protected function setUp() { $this->objectManager = new ObjectManager($this); + $this->contextMock = $this->getMockBuilder(Context::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->requestMock = $this->getMockBuilder(HttpRequest::class) + ->disableOriginalConstructor() + ->getMock(); + $this->model = $this->objectManager->getObject( + Identifier::class, + [ + 'request' => $this->requestMock, + 'context' => $this->contextMock, + ] + ); } - /** - * @param string $uri - * @param string|null $vary - * @return \Magento\Framework\App\Request\Http - */ - protected function getRequestMock($uri, $vary = null) + public function testSecureDifferentiator() { - $requestMock = $this->getMock('\Magento\Framework\App\Request\Http', [], [], '', false); - $requestMock->expects($this->once()) + $this->requestMock->expects($this->at(0)) + ->method('isSecure') + ->willReturn(true); + $this->requestMock->expects($this->at(3)) ->method('isSecure') ->willReturn(false); - $requestMock->expects($this->once()) - ->method('getRequestUri') - ->willReturn($uri); - $requestMock->expects($this->once()) - ->method('get') - ->with($this->equalTo(\Magento\Framework\App\Response\Http::COOKIE_VARY_STRING)) - ->willReturn($vary); - return $requestMock; + $this->requestMock->method('getUriString') + ->willReturn('http://example.com/path/'); + $this->contextMock->method('getVaryString')->willReturn(self::VARY); + + $valueWithSecureRequest = $this->model->getValue(); + $valueWithInsecureRequest = $this->model->getValue(); + $this->assertNotEquals($valueWithSecureRequest, $valueWithInsecureRequest); } - /** - * @param int $getVeryStringCalledTimes - * @param string|null $vary - * @return \Magento\Framework\App\Http\Context - */ - protected function getContextMock($getVeryStringCalledTimes, $vary) + public function testDomainDifferentiator() { - $contextMock = $this->getMock('\Magento\Framework\App\Http\Context', [], [], '', false); - $contextMock->expects($this->exactly($getVeryStringCalledTimes)) - ->method('getVaryString') - ->willReturn($vary); - return $contextMock; + $this->requestMock->method('isSecure')->willReturn(true); + $this->requestMock->expects($this->at(1)) + ->method('getUriString') + ->willReturn('http://example.com/path/'); + $this->requestMock->expects($this->at(4)) + ->method('getUriString') + ->willReturn('http://example.net/path/'); + $this->contextMock->method('getVaryString')->willReturn(self::VARY); + + $valueDomain1 = $this->model->getValue(); + $valueDomain2 = $this->model->getValue(); + $this->assertNotEquals($valueDomain1, $valueDomain2); } - /** - * @param string $uri - * @param string|null $varyStringCookie - * @param string|null $varyStringContext - * @param string $expected - * @dataProvider dataProvider - */ - public function testGetValue($uri, $varyStringCookie, $varyStringContext, $expected) + public function testPathDifferentiator() { - $request = $this->getRequestMock($uri, $varyStringCookie); - $context = $this->getContextMock($varyStringCookie ? 0 : 1, $varyStringContext); + $this->requestMock->method('isSecure')->willReturn(true); + $this->requestMock->expects($this->at(1)) + ->method('getUriString') + ->willReturn('http://example.com/path/'); + $this->requestMock->expects($this->at(4)) + ->method('getUriString') + ->willReturn('http://example.com/path1/'); + $this->contextMock->method('getVaryString')->willReturn(self::VARY); - $model = $this->objectManager->getObject( - '\Magento\Framework\App\PageCache\Identifier', - [ - 'request' => $request, - 'context' => $context, - ] - ); - $this->assertEquals($expected, $model->getValue()); + $valuePath1 = $this->model->getValue(); + $valuePath2 = $this->model->getValue(); + $this->assertNotEquals($valuePath1, $valuePath2); } /** - * @return array + * @param $cookieExists + * + * @dataProvider trueFalseDataProvider */ - public function dataProvider() + public function testVaryStringSource($cookieExists) + { + $this->requestMock->method('get')->willReturn($cookieExists ? 'vary-string-from-cookie' : null); + $this->contextMock->expects($cookieExists ? $this->never() : $this->once())->method('getVaryString'); + $this->model->getValue(); + } + + public function trueFalseDataProvider() { - $uri = 'http://domain.com/customer'; - $vary = 1; - $data = [false, $uri, $vary]; - ksort($data); - $expected = md5(serialize($data)); - - return [ - [$uri, $vary, null, $expected], - [$uri, null, $vary, $expected] - ]; + return [[true], [false]]; } } diff --git a/nginx.conf.sample b/nginx.conf.sample index d4cdc8a9934dd..3f9eaba55defa 100644 --- a/nginx.conf.sample +++ b/nginx.conf.sample @@ -1,12 +1,4 @@ -# Magento Vars -# set $MAGE_ROOT /path/to/magento/root; -# -# ############################################ -## Optional override of deployment mode. We recommend you use the -## command bin/magento deploy:mode:set to switch modes instead -## set $MAGE_MODE default; # or production or developer -# -# Example configuration: +## Example configuration: # upstream fastcgi_backend { # # use tcp connection # # server 127.0.0.1:9000; @@ -19,7 +11,19 @@ # set $MAGE_ROOT /var/www/magento2; # include /vagrant/magento2/nginx.conf.sample; # } - +# +## Optional override of deployment mode. We recommend you use the +## command 'bin/magento deploy:mode:set' to switch modes instead. +## +## set $MAGE_MODE default; # or production or developer +## +## If you set MAGE_MODE in server config, you must pass the variable into the +## PHP entry point blocks, which are indicated below. You can pass +## it in using: +## +## fastcgi_param MAGE_MODE $MAGE_MODE; +## +## In production mode, you should uncomment the 'expires' directive in the /static/ location block root $MAGE_ROOT/pub; @@ -28,6 +32,7 @@ autoindex off; charset UTF-8; #add_header "X-UA-Compatible" "IE=Edge"; +# PHP entry point for setup application location ~* ^/setup($|/) { root $MAGE_ROOT; location ~ ^/setup/index.php { @@ -46,6 +51,7 @@ location ~* ^/setup($|/) { } } +# PHP entry point for update application location ~* ^/update($|/) { root $MAGE_ROOT; @@ -81,9 +87,8 @@ location /pub/ { } location /static/ { - if ($MAGE_MODE = "production") { - expires max; - } + # Uncomment the following line in production mode + # expires max; # Remove signature of the static files that is used to overcome the browser cache location ~ ^/static/version { @@ -148,6 +153,7 @@ location /media/import/ { deny all; } +# PHP entry point for main application location ~ (index|get|static|report|404|503)\.php$ { try_files $uri =404; fastcgi_pass fastcgi_backend; @@ -157,7 +163,6 @@ location ~ (index|get|static|report|404|503)\.php$ { fastcgi_param PHP_VALUE "memory_limit=768M \n max_execution_time=600"; fastcgi_read_timeout 600s; fastcgi_connect_timeout 600s; - fastcgi_param MAGE_MODE $MAGE_MODE; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;