diff --git a/.rector.php b/.rector.php index 9ec431be384..93f6803053c 100644 --- a/.rector.php +++ b/.rector.php @@ -45,7 +45,7 @@ false, false, false, - false, + true, false, false, false, diff --git a/app/Mage.php b/app/Mage.php index dd016e03030..af8e1224b38 100644 --- a/app/Mage.php +++ b/app/Mage.php @@ -781,7 +781,7 @@ public static function run($code = '', $type = 'store', $options = []) * * @param array $options */ - protected static function _setIsInstalled($options = []) + private static function _setIsInstalled($options = []) { if (isset($options['is_installed']) && $options['is_installed']) { self::$_isInstalled = true; @@ -793,7 +793,7 @@ protected static function _setIsInstalled($options = []) * * @param array $options */ - protected static function _setConfigModel($options = []) + private static function _setConfigModel($options = []) { if (isset($options['config_model']) && class_exists($options['config_model'])) { $alternativeConfigModelName = $options['config_model']; diff --git a/app/code/core/Mage/Bundle/Model/Product/Type.php b/app/code/core/Mage/Bundle/Model/Product/Type.php index b460eb8d201..028a1a916ae 100644 --- a/app/code/core/Mage/Bundle/Model/Product/Type.php +++ b/app/code/core/Mage/Bundle/Model/Product/Type.php @@ -317,7 +317,7 @@ public function save($product = null) $selection['selection_id'] = $selectionModel->getSelectionId(); - if ($selectionModel->getSelectionId()) { + if ($selectionModel->getSelectionId() && !$selectionModel->isDeleted()) { $excludeSelectionIds[] = $selectionModel->getSelectionId(); $usedProductIds[] = $selectionModel->getProductId(); } diff --git a/app/code/core/Mage/Catalog/Helper/Product/Url.php b/app/code/core/Mage/Catalog/Helper/Product/Url.php index 80f726dc636..4355433ce36 100644 --- a/app/code/core/Mage/Catalog/Helper/Product/Url.php +++ b/app/code/core/Mage/Catalog/Helper/Product/Url.php @@ -85,6 +85,10 @@ class Mage_Catalog_Helper_Product_Url extends Mage_Core_Helper_Url 'צ' => 'c', 'ק' => 'q', 'ר' => 'r', 'ש' => 'w', 'ת' => 't', '™' => 'tm', ]; + protected array $_convertTableShort = ['@' => 'at', '©' => 'c', '®' => 'r', '™' => 'tm']; + + protected array $_convertTableCustom = []; + /** * Check additional instruction for conversion table in configuration */ @@ -93,7 +97,9 @@ public function __construct() $convertNode = Mage::getConfig()->getNode('default/url/convert'); if ($convertNode) { foreach ($convertNode->children() as $node) { - $this->_convertTable[(string) $node->from] = (string) $node->to; + if (property_exists($node, 'from') && property_exists($node, 'to')) { + $this->_convertTableCustom[(string) $node->from] = (string) $node->to; + } } } } @@ -105,7 +111,17 @@ public function __construct() */ public function getConvertTable() { - return $this->_convertTable; + return $this->_convertTable + $this->_convertTableShort + $this->_convertTableCustom; + } + + public function getConvertTableCustom(): array + { + return $this->_convertTableCustom; + } + + public function getConvertTableShort(): array + { + return $this->_convertTableShort + $this->_convertTableCustom; } /** @@ -113,6 +129,8 @@ public function getConvertTable() * * @param string $string * @return string + * @deprecated + * @see Mage_Catalog_Model_Url::formatUrlKey() */ public function format($string) { diff --git a/app/code/core/Mage/Catalog/Model/Attribute/Backend/Urlkey/Abstract.php b/app/code/core/Mage/Catalog/Model/Attribute/Backend/Urlkey/Abstract.php index 5b9f39353f2..4d37db0d85e 100644 --- a/app/code/core/Mage/Catalog/Model/Attribute/Backend/Urlkey/Abstract.php +++ b/app/code/core/Mage/Catalog/Model/Attribute/Backend/Urlkey/Abstract.php @@ -40,6 +40,11 @@ public function beforeSave($object) $urlKey = $object->getName(); } + if (method_exists($object, 'setLocale')) { + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $object->getStoreId()); + $object->setLocale($locale); + } + $object->setData($attributeName, $object->formatUrlKey($urlKey)); return $this; diff --git a/app/code/core/Mage/Catalog/Model/Category.php b/app/code/core/Mage/Catalog/Model/Category.php index c7f913cee6c..4a1e89bc017 100644 --- a/app/code/core/Mage/Catalog/Model/Category.php +++ b/app/code/core/Mage/Catalog/Model/Category.php @@ -163,6 +163,9 @@ class Mage_Catalog_Model_Category extends Mage_Catalog_Model_Abstract */ protected $_urlModel; + + protected ?string $locale = null; + /** * Initialize resource mode */ @@ -501,9 +504,10 @@ public function getUrlModel() public function getCategoryIdUrl() { Varien_Profiler::start('REGULAR: ' . __METHOD__); - $urlKey = $this->getUrlKey() ? $this->getUrlKey() : $this->formatUrlKey($this->getName()); + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $this->getStoreId()); + $urlKey = $this->getUrlKey() ? $this->getUrlKey() : $this->setLocale($locale)->formatUrlKey($this->getName()); $url = $this->getUrlInstance()->getUrl('catalog/category/view', [ - 's' => $urlKey, + 's' => $urlKey, 'id' => $this->getId(), ]); Varien_Profiler::stop('REGULAR: ' . __METHOD__); @@ -518,11 +522,18 @@ public function getCategoryIdUrl() */ public function formatUrlKey($str) { - $str = Mage::helper('catalog/product_url')->format($str); - $urlKey = preg_replace('#[^0-9a-z]+#i', '-', $str); - $urlKey = strtolower($urlKey); - $urlKey = trim($urlKey, '-'); - return $urlKey; + return $this->getUrlModel()->setLocale($this->getLocale())->formatUrlKey($str); + } + + public function getLocale(): ?string + { + return $this->locale; + } + + public function setLocale(?string $locale) + { + $this->locale = $locale; + return $this; } /** diff --git a/app/code/core/Mage/Catalog/Model/Category/Url.php b/app/code/core/Mage/Catalog/Model/Category/Url.php index 7c2994041e2..968393c65bf 100644 --- a/app/code/core/Mage/Catalog/Model/Category/Url.php +++ b/app/code/core/Mage/Catalog/Model/Category/Url.php @@ -15,34 +15,13 @@ */ /** - * Catalog category url + * Catalog Url model * * @category Mage * @package Mage_Catalog */ -class Mage_Catalog_Model_Category_Url +class Mage_Catalog_Model_Category_Url extends Mage_Catalog_Model_Url { - /** - * Url instance - * - * @var Mage_Core_Model_Url - */ - protected $_url; - - /** - * Factory instance - * - * @var Mage_Catalog_Model_Factory - */ - protected $_factory; - - /** - * Url rewrite instance - * - * @var Mage_Core_Model_Url_Rewrite - */ - protected $_urlRewrite; - /** * Initialize Url model */ @@ -113,32 +92,4 @@ protected function _getRequestPath(Mage_Catalog_Model_Category $category) } return false; } - - /** - * Retrieve Url instance - * - * @return Mage_Core_Model_Url - */ - public function getUrlInstance() - { - if ($this->_url === null) { - /** @var Mage_Core_Model_Url $model */ - $model = $this->_factory->getModel('core/url'); - $this->_url = $model; - } - return $this->_url; - } - - /** - * Retrieve Url rewrite instance - * - * @return Mage_Core_Model_Url_Rewrite - */ - public function getUrlRewrite() - { - if ($this->_urlRewrite === null) { - $this->_urlRewrite = $this->_factory->getUrlRewriteInstance(); - } - return $this->_urlRewrite; - } } diff --git a/app/code/core/Mage/Catalog/Model/Product.php b/app/code/core/Mage/Catalog/Model/Product.php index ac9586b2447..e702cb4dd35 100644 --- a/app/code/core/Mage/Catalog/Model/Product.php +++ b/app/code/core/Mage/Catalog/Model/Product.php @@ -339,6 +339,8 @@ class Mage_Catalog_Model_Product extends Mage_Catalog_Model_Abstract */ protected $_reviewSummary = []; + protected ?string $locale = null; + /** * Initialize resources */ @@ -1681,7 +1683,18 @@ public function getUrlInStore($params = []) */ public function formatUrlKey($str) { - return $this->getUrlModel()->formatUrlKey($str); + return $this->getUrlModel()->setLocale($this->getLocale())->formatUrlKey($str); + } + + public function getLocale(): ?string + { + return $this->locale; + } + + public function setLocale(?string $locale) + { + $this->locale = $locale; + return $this; } /** diff --git a/app/code/core/Mage/Catalog/Model/Product/Url.php b/app/code/core/Mage/Catalog/Model/Product/Url.php index 018d17a2716..d03d36db4e1 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Url.php +++ b/app/code/core/Mage/Catalog/Model/Product/Url.php @@ -20,31 +20,10 @@ * @category Mage * @package Mage_Catalog */ -class Mage_Catalog_Model_Product_Url extends Varien_Object +class Mage_Catalog_Model_Product_Url extends Mage_Catalog_Model_Url { public const CACHE_TAG = 'url_rewrite'; - /** - * URL instance - * - * @var Mage_Core_Model_Url - */ - protected $_url; - - /** - * URL Rewrite Instance - * - * @var Mage_Core_Model_Url_Rewrite - */ - protected $_urlRewrite; - - /** - * Factory instance - * - * @var Mage_Catalog_Model_Factory - */ - protected $_factory; - /** * @var Mage_Core_Model_Store */ @@ -59,32 +38,6 @@ public function __construct(array $args = []) $this->_store = !empty($args['store']) ? $args['store'] : Mage::app()->getStore(); } - /** - * Retrieve URL Instance - * - * @return Mage_Core_Model_Url - */ - public function getUrlInstance() - { - if ($this->_url === null) { - $this->_url = Mage::getModel('core/url'); - } - return $this->_url; - } - - /** - * Retrieve URL Rewrite Instance - * - * @return Mage_Core_Model_Url_Rewrite - */ - public function getUrlRewrite() - { - if ($this->_urlRewrite === null) { - $this->_urlRewrite = $this->_factory->getUrlRewriteInstance(); - } - return $this->_urlRewrite; - } - /** * 'no_selection' shouldn't be a valid image attribute value * @@ -132,21 +85,6 @@ public function getProductUrl($product, $useSid = null) return $this->getUrl($product, $params); } - /** - * Format Key for URL - * - * @param string $str - * @return string - */ - public function formatUrlKey($str) - { - $urlKey = preg_replace('#[^0-9a-z]+#i', '-', Mage::helper('catalog/product_url')->format($str)); - $urlKey = strtolower($urlKey); - $urlKey = trim($urlKey, '-'); - - return $urlKey; - } - /** * Retrieve Product Url path (with category if exists) * @@ -281,7 +219,6 @@ protected function _getRequestPath($product, $categoryId) if ($rewrite->getId()) { return $rewrite->getRequestPath(); } - return false; } } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Attribute/Backend/Urlkey.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Attribute/Backend/Urlkey.php index dde773c6866..ba08ad49f5d 100644 --- a/app/code/core/Mage/Catalog/Model/Resource/Product/Attribute/Backend/Urlkey.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Attribute/Backend/Urlkey.php @@ -37,6 +37,10 @@ public function beforeSave($object) $urlKey = $object->getName(); } + if (method_exists($object, 'setLocale')) { + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $object->getStoreId()); + $object->setLocale($locale); + } $object->setData($attributeName, $object->formatUrlKey($urlKey)); return $this; diff --git a/app/code/core/Mage/Catalog/Model/Url.php b/app/code/core/Mage/Catalog/Model/Url.php index 61cf0de059a..595bad6cd77 100644 --- a/app/code/core/Mage/Catalog/Model/Url.php +++ b/app/code/core/Mage/Catalog/Model/Url.php @@ -14,13 +14,15 @@ * @license https://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ +use Symfony\Component\String\Slugger\AsciiSlugger; + /** * Catalog url model * * @category Mage * @package Mage_Catalog */ -class Mage_Catalog_Model_Url +class Mage_Catalog_Model_Url extends Varien_Object { /** * Number of characters allowed to be in URL path @@ -100,6 +102,62 @@ class Mage_Catalog_Model_Url */ protected static $_categoryForUrlPath; + protected ?string $locale = null; + + /** + * Url instance + * + * @var Mage_Core_Model_Url + */ + protected $_url; + + /** + * Url rewrite instance + * + * @var Mage_Core_Model_Url_Rewrite + */ + protected $_urlRewrite; + + /** + * Factory instance + * + * @var Mage_Catalog_Model_Factory + */ + protected $_factory; + + /** + * @var AsciiSlugger[] + */ + protected ?array $slugger = null; + + /** + * Retrieve Url instance + * + * @return Mage_Core_Model_Url + */ + public function getUrlInstance() + { + if ($this->_url === null) { + /** @var Mage_Core_Model_Url $model */ + $model = $this->_factory->getModel('core/url'); + $this->_url = $model; + } + return $this->_url; + } + + /** + * Retrieve Url rewrite instance + * + * @return Mage_Core_Model_Url_Rewrite + */ + public function getUrlRewrite() + { + if ($this->_urlRewrite === null) { + $this->_urlRewrite = $this->_factory->getUrlRewriteInstance(); + } + return $this->_urlRewrite; + } + /** * Adds url_path property for non-root category - to ensure that url path is not empty. * @@ -257,11 +315,9 @@ public function refreshRewrites($storeId = null) protected function _refreshCategoryRewrites(Varien_Object $category, $parentPath = null, $refreshProducts = true) { if ($category->getId() != $this->getStores($category->getStoreId())->getRootCategoryId()) { - if ($category->getUrlKey() == '') { - $urlKey = $this->getCategoryModel()->formatUrlKey($category->getName()); - } else { - $urlKey = $this->getCategoryModel()->formatUrlKey($category->getUrlKey()); - } + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $category->getStoreId()); + $urlKey = $category->getUrlKey() == '' ? $category->getName() : $category->getUrlKey(); + $urlKey = $this->getCategoryModel()->setLocale($locale)->formatUrlKey($urlKey); $idPath = $this->generatePath('id', null, $category); $targetPath = $this->generatePath('target', null, $category); @@ -320,11 +376,10 @@ protected function _refreshProductRewrite(Varien_Object $product, Varien_Object if ($category->getId() == $category->getPath()) { return $this; } - if ($product->getUrlKey() == '') { - $urlKey = $this->getProductModel()->formatUrlKey($product->getName()); - } else { - $urlKey = $this->getProductModel()->formatUrlKey($product->getUrlKey()); - } + + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $product->getStoreId()); + $urlKey = $product->getUrlKey() == '' ? $product->getName() : $product->getUrlKey(); + $urlKey = $this->getProductModel()->setLocale($locale)->formatUrlKey($urlKey); $idPath = $this->generatePath('id', $product, $category); $targetPath = $this->generatePath('target', $product, $category); @@ -366,7 +421,7 @@ protected function _refreshProductRewrite(Varien_Object $product, Varien_Object } /** - * Refresh products for catwgory + * Refresh products for category * * @return $this */ @@ -672,7 +727,7 @@ public function getUnusedPathByUrlKey($storeId, $requestPath, $idPath, $urlKey) } /** - * Retrieve product rewrite sufix for store + * Retrieve product rewrite suffix for store * * @param int $storeId * @return string @@ -683,7 +738,7 @@ public function getProductUrlSuffix($storeId) } /** - * Retrieve category rewrite sufix for store + * Retrieve category rewrite suffix for store * * @param int $storeId * @return string @@ -710,11 +765,9 @@ public function getCategoryRequestPath($category, $parentPath) $existingRequestPath = $this->_rewrites[$idPath]->getRequestPath(); } - if ($category->getUrlKey() == '') { - $urlKey = $this->getCategoryModel()->formatUrlKey($category->getName()); - } else { - $urlKey = $this->getCategoryModel()->formatUrlKey($category->getUrlKey()); - } + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $category->getStoreId()); + $urlKey = $category->getUrlKey() == '' ? $category->getName() : $category->getUrlKey(); + $urlKey = $this->getCategoryModel()->setLocale($locale)->formatUrlKey($urlKey); $categoryUrlSuffix = $this->getCategoryUrlSuffix($storeId); if ($parentPath === null) { @@ -766,11 +819,10 @@ protected function _deleteOldTargetPath($requestPath, $idPath, $storeId) */ public function getProductRequestPath($product, $category) { - if ($product->getUrlKey() == '') { - $urlKey = $this->getProductModel()->formatUrlKey($product->getName()); - } else { - $urlKey = $this->getProductModel()->formatUrlKey($product->getUrlKey()); - } + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $product->getStoreId()); + $urlKey = $product->getUrlKey() == '' ? $product->getName() : $product->getUrlKey(); + $urlKey = $this->getProductModel()->setLocale($locale)->formatUrlKey($urlKey); + $storeId = $category->getStoreId(); $suffix = $this->getProductUrlSuffix($storeId); $idPath = $this->generatePath('id', $product, $category); @@ -881,11 +933,9 @@ public function generatePath($type = 'target', $product = null, $category = null if ($type === 'request') { // for category if (!$product) { - if ($category->getUrlKey() == '') { - $urlKey = $this->getCategoryModel()->formatUrlKey($category->getName()); - } else { - $urlKey = $this->getCategoryModel()->formatUrlKey($category->getUrlKey()); - } + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $category->getStoreId()); + $urlKey = $category->getUrlKey() == '' ? $category->getName() : $category->getUrlKey(); + $urlKey = $this->getCategoryModel()->setLocale($locale)->formatUrlKey($urlKey); $categoryUrlSuffix = $this->getCategoryUrlSuffix($category->getStoreId()); if ($parentPath === null) { @@ -912,11 +962,10 @@ public function generatePath($type = 'target', $product = null, $category = null Mage::throwException(Mage::helper('core')->__('A category object is required for determining the product request path.')); // why? } - if ($product->getUrlKey() == '') { - $urlKey = $this->getProductModel()->formatUrlKey($product->getName()); - } else { - $urlKey = $this->getProductModel()->formatUrlKey($product->getUrlKey()); - } + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $product->getStoreId()); + $urlKey = $product->getUrlKey() == '' ? $product->getName() : $product->getUrlKey(); + $urlKey = $this->getProductModel()->setLocale($locale)->formatUrlKey($urlKey); + $productUrlSuffix = $this->getProductUrlSuffix($category->getStoreId()); if ($category->getLevel() > 1) { // To ensure, that category has url path either from attribute or generated now @@ -984,4 +1033,64 @@ protected function _saveRewriteHistory($rewriteData, $rewrite) return $this; } + + /** + * Format Key for URL + * + * @param string $str + * @return string + */ + public function formatUrlKey($str) + { + return $this->getSlugger()->slug($str)->lower()->toString(); + } + + public function getSlugger(): AsciiSlugger + { + $locale = $this->getLocale(); + if (is_null($this->slugger) || !array_key_exists($locale, $this->slugger)) { + $config = $this->getSluggerConfig($locale); + $slugger = new AsciiSlugger('en', $config); + $slugger->setLocale($locale); + + $this->slugger[$locale] = $slugger; + } + + return $this->slugger[$locale]; + } + + final public function getSluggerConfig(?string $locale): array + { + $config = Mage::helper('catalog/product_url')->getConvertTableShort(); + + if ($locale) { + $convertNode = Mage::getConfig()->getNode('default/url/convert/' . $locale); + if ($convertNode instanceof Mage_Core_Model_Config_Element) { + $localeConfig = []; + /** @var Mage_Core_Model_Config_Element $node */ + foreach ($convertNode->children() as $node) { + if (property_exists($node, 'from') && property_exists($node, 'to')) { + $localeConfig[(string) $node->from] = (string) $node->to; + } + } + $config = [$locale => $config + $localeConfig]; + } + } + + return $config; + } + + public function getLocale(): ?string + { + return $this->locale; + } + + /** + * @return $this + */ + public function setLocale(?string $locale) + { + $this->locale = $locale; + return $this; + } } diff --git a/app/code/core/Mage/Core/etc/config.xml b/app/code/core/Mage/Core/etc/config.xml index d7ce041219d..1af6d04697a 100644 --- a/app/code/core/Mage/Core/etc/config.xml +++ b/app/code/core/Mage/Core/etc/config.xml @@ -507,6 +507,60 @@ + + + + + + prozent + + + + und + + + + + + percent + + + + and + + + + + + pour cent + + + + et + + + + + + per cento + + + + e + + + + + + por ciento + + + + et + + + + diff --git a/app/code/core/Mage/ImportExport/Model/Import/Entity/Product.php b/app/code/core/Mage/ImportExport/Model/Import/Entity/Product.php index 902a887ca53..f9eaf4d7847 100644 --- a/app/code/core/Mage/ImportExport/Model/Import/Entity/Product.php +++ b/app/code/core/Mage/ImportExport/Model/Import/Entity/Product.php @@ -1560,7 +1560,8 @@ protected function _prepareAttributes($rowData, $rowScope, $attributes, $rowSku, $attrValue = gmdate(Varien_Date::DATETIME_PHP_FORMAT, strtotime($attrValue)); } elseif ($attribute->getAttributeCode() === 'url_key') { if (empty($attrValue)) { - $attrValue = $product->formatUrlKey($product->getName()); + $locale = Mage::getStoreConfig(Mage_Core_Model_Locale::XML_PATH_DEFAULT_LOCALE, $product->getStoreId()); + $attrValue = $product->setLocale($locale)->formatUrlKey($product->getName()); } } elseif ($backModel) { $attribute->getBackend()->beforeSave($product); diff --git a/app/code/core/Mage/Sales/Model/Quote/Item.php b/app/code/core/Mage/Sales/Model/Quote/Item.php index 3fa2278aeb4..aed737200e3 100644 --- a/app/code/core/Mage/Sales/Model/Quote/Item.php +++ b/app/code/core/Mage/Sales/Model/Quote/Item.php @@ -516,12 +516,16 @@ public function compare($item) // dispose of some options params, that can cramp comparing of arrays if (is_string($itemOptionValue) && is_string($optionValue)) { try { - /** @var Unserialize_Parser $parser */ + /** + * @var Mage_Core_Helper_UnserializeArray $parser + * @var Mage_Core_Helper_String $stringHelper + */ $parser = Mage::helper('core/unserializeArray'); + $stringHelper = Mage::helper('core/string'); - $_itemOptionValue = - is_numeric($itemOptionValue) ? $itemOptionValue : $parser->unserialize($itemOptionValue); - $_optionValue = is_numeric($optionValue) ? $optionValue : $parser->unserialize($optionValue); + // only ever try to unserialize, if it looks like a serialized array + $_itemOptionValue = $stringHelper->isSerializedArrayOrObject($itemOptionValue) ? $parser->unserialize($itemOptionValue) : $itemOptionValue; + $_optionValue = $stringHelper->isSerializedArrayOrObject($optionValue) ? $parser->unserialize($optionValue) : $optionValue; if (is_array($_itemOptionValue) && is_array($_optionValue)) { $itemOptionValue = $_itemOptionValue; diff --git a/composer.json b/composer.json index 04e2a765d6d..9d754ab7731 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,9 @@ "symfony/polyfill-php81": "^1.31", "symfony/polyfill-php82": "^1.31", "symfony/polyfill-php83": "^1.31", - "symfony/polyfill-php84": "^1.31" + "symfony/polyfill-php84": "^1.31", + "symfony/string": "^5.4", + "symfony/translation-contracts": "^2.5" }, "require-dev": { "ext-xmlreader": "*", diff --git a/composer.lock b/composer.lock index 090ee37b85b..6fd160cdc5a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4aaaae1bdb9cd2d0f253b63e8c1eee65", + "content-hash": "af2f1a89f41c33d8f756c32152ac217d", "packages": [ { "name": "colinmollenhour/cache-backend-redis", @@ -2372,6 +2372,84 @@ } ], "time": "2024-11-10T20:33:58+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v2.5.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "b0073a77ac0b7ea55131020e87b1e3af540f4664" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b0073a77ac0b7ea55131020e87b1e3af540f4664", + "reference": "b0073a77ac0b7ea55131020e87b1e3af540f4664", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T13:51:25+00:00" } ], "packages-dev": [ @@ -7261,7 +7339,7 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/tests/unit/Mage/Catalog/Helper/Product/UrlTest.php b/tests/unit/Mage/Catalog/Helper/Product/UrlTest.php index 784b02ca909..9aa95935e2d 100644 --- a/tests/unit/Mage/Catalog/Helper/Product/UrlTest.php +++ b/tests/unit/Mage/Catalog/Helper/Product/UrlTest.php @@ -33,16 +33,40 @@ public function setUp(): void } /** + * @covers Mage_Catalog_Helper_Product_Url::getConvertTable() * @group Mage_Catalog * @group Mage_Catalog_Helper */ public function testGetConvertTable(): void { - $this->assertIsArray($this->subject->getConvertTable()); - $this->assertCount(317, $this->subject->getConvertTable()); + $result = $this->subject->getConvertTable(); + $this->assertCount(317, $result); } /** + * @covers Mage_Catalog_Helper_Product_Url::getConvertTableCustom() + * @group Mage_Catalog + * @group Mage_Catalog_Helper + */ + public function testGetConvertTableCustom(): void + { + $result = $this->subject->getConvertTableCustom(); + $this->assertCount(0, $result); + } + + /** + * @covers Mage_Catalog_Helper_Product_Url::getConvertTableShort() + * @group Mage_Catalog + * @group Mage_Catalog_Helper + */ + public function testGetConvertTableShort(): void + { + $result = $this->subject->getConvertTableShort(); + $this->assertCount(4, $result); + } + + /** + * @covers Mage_Catalog_Helper_Product_Url::format() * @dataProvider provideFormat * @group Mage_Catalog * @group Mage_Catalog_Helper @@ -56,13 +80,17 @@ public function provideFormat(): Generator { yield 'null' => [ '', - null + null, + ]; + yield 'string' => [ + 'string', + 'string', ]; - yield '&' => [ - 'and', - '&', + yield 'umlauts' => [ + 'string with aou', + 'string with ÄÖÜ', ]; - yield '@' => [ + yield 'at' => [ 'at', '@', ]; diff --git a/tests/unit/Mage/Catalog/Model/CategoryTest.php b/tests/unit/Mage/Catalog/Model/CategoryTest.php index f4451161abe..05d71ef69cc 100644 --- a/tests/unit/Mage/Catalog/Model/CategoryTest.php +++ b/tests/unit/Mage/Catalog/Model/CategoryTest.php @@ -17,13 +17,18 @@ namespace OpenMage\Tests\Unit\Mage\Catalog\Model; +use Generator; use Mage; use Mage_Catalog_Model_Category; +use Mage_Catalog_Model_Category_Url; use Mage_Catalog_Model_Resource_Product_Collection; +use Mage_Catalog_Model_Url; use PHPUnit\Framework\TestCase; class CategoryTest extends TestCase { + public const TEST_STRING = 'a & B, x%, ä, ö, ü'; + public Mage_Catalog_Model_Category $subject; public function setUp(): void @@ -85,4 +90,50 @@ public function testAfterCommitCallback(): void { $this->assertInstanceOf(Mage_Catalog_Model_Category::class, $this->subject->afterCommitCallback()); } + + /** + * @group Mage_Catalog + * @group Mage_Catalog_Model + */ + public function testGetUrlModel(): void + { + $this->assertInstanceOf(Mage_Catalog_Model_Url::class, $this->subject->getUrlModel()); + $this->assertInstanceOf(Mage_Catalog_Model_Category_Url::class, $this->subject->getUrlModel()); + } + + /** + * @dataProvider provideFormatUrlKey + * @group Mage_Catalog + * @group Mage_Catalog_Model + * @runInSeparateProcess + */ +// public function testGetCategoryIdUrl($expectedResult, ?string $locale): void +// { +// $this->subject->setName(self::TEST_STRING); +// $this->subject->setLocale($locale); +// $this->assertSame($expectedResult, $this->subject->getCategoryIdUrl()); +// } + + /** + * @dataProvider provideFormatUrlKey + * @group Mage_Catalog + * @group Mage_Catalog_Model + */ + public function testFormatUrlKey($expectedResult, ?string $locale): void + { + $this->subject->setLocale($locale); + $this->assertSame($expectedResult, $this->subject->formatUrlKey(self::TEST_STRING)); + } + + public function provideFormatUrlKey(): Generator + { + yield 'null locale' => [ + 'a-b-x-a-o-u', + null, + ]; + yield 'de_DE' => [ + 'a-und-b-x-prozent-ae-oe-ue', + 'de_DE', + ]; + } } diff --git a/tests/unit/Mage/Catalog/Model/ProductTest.php b/tests/unit/Mage/Catalog/Model/ProductTest.php index 240cc9a56b9..df1d4243de9 100644 --- a/tests/unit/Mage/Catalog/Model/ProductTest.php +++ b/tests/unit/Mage/Catalog/Model/ProductTest.php @@ -24,10 +24,13 @@ use Mage_Catalog_Model_Product_Type_Abstract; use Mage_Catalog_Model_Product_Url; use Mage_Catalog_Model_Resource_Product_Collection; +use Mage_Catalog_Model_Url; use PHPUnit\Framework\TestCase; class ProductTest extends TestCase { + public const TEST_STRING = 'a & B, x%, ä, ö, ü'; + public Mage_Catalog_Model_Product $subject; public function setUp(): void @@ -60,6 +63,7 @@ public function testGetResourceCollection(): void */ public function testGetUrlModel(): void { + $this->assertInstanceOf(Mage_Catalog_Model_Url::class, $this->subject->getUrlModel()); $this->assertInstanceOf(Mage_Catalog_Model_Product_Url::class, $this->subject->getUrlModel()); } @@ -164,4 +168,27 @@ public function testAfterCommitCallback(): void { $this->assertInstanceOf(Mage_Catalog_Model_Product::class, $this->subject->afterCommitCallback()); } + + /** + * @dataProvider provideFormatUrlKey + * @group Mage_Catalog + * @group Mage_Catalog_Model + */ + public function testFormatUrlKey($expectedResult, ?string $locale): void + { + $this->subject->setLocale($locale); + $this->assertSame($expectedResult, $this->subject->formatUrlKey(self::TEST_STRING)); + } + + public function provideFormatUrlKey(): Generator + { + yield 'null locale' => [ + 'a-b-x-a-o-u', + null, + ]; + yield 'de_DE' => [ + 'a-und-b-x-prozent-ae-oe-ue', + 'de_DE', + ]; + } } diff --git a/tests/unit/Mage/Catalog/Model/UrlTest.php b/tests/unit/Mage/Catalog/Model/UrlTest.php index aad8f4a00e1..7e04d8d7113 100644 --- a/tests/unit/Mage/Catalog/Model/UrlTest.php +++ b/tests/unit/Mage/Catalog/Model/UrlTest.php @@ -22,10 +22,13 @@ use Mage_Catalog_Model_Url; use Mage_Core_Exception; use PHPUnit\Framework\TestCase; +use Symfony\Component\String\Slugger\AsciiSlugger; use Varien_Object; class UrlTest extends TestCase { + public const TEST_STRING = '--a & B, x% @ ä ö ü ™--'; + public Mage_Catalog_Model_Url $subject; public function setUp(): void @@ -93,10 +96,12 @@ public function provideGeneratePathData(): Generator 'id' => '999', 'store_id' => '1', 'url_key' => '', + 'name' => 'category', ]); $product = new Varien_Object([ - 'id' => '999' + 'id' => '999', + 'name' => 'product', ]); yield 'test exception' => [ @@ -106,7 +111,7 @@ public function provideGeneratePathData(): Generator null, ]; yield 'request' => [ - '-.html', + 'product.html', 'request', $product, $category, @@ -130,4 +135,92 @@ public function provideGeneratePathData(): Generator $category, ]; } + /** + * @dataProvider provideFormatUrlKey + * @group Mage_Catalog + * @group Mage_Catalog_Model + */ + public function testFormatUrlKey($expectedResult, string $locale): void + { + $this->subject->setLocale($locale); + $this->assertSame($expectedResult, $this->subject->formatUrlKey(self::TEST_STRING)); + } + + public function provideFormatUrlKey(): Generator + { + yield 'de_DE' => [ + 'a-und-b-x-prozent-at-ae-oe-ue-tm', + 'de_DE', + ]; + yield 'en_US' => [ + 'a-and-b-x-percent-at-a-o-u-tm', + 'en_US', + ]; + yield 'es_ES' => [ + 'a-et-b-x-por-ciento-at-a-o-u-tm', + 'es_ES', + ]; + yield 'fr_FR' => [ + 'a-et-b-x-pour-cent-at-a-o-u-tm', + 'fr_FR', + ]; + yield 'it_IT' => [ + 'a-e-b-x-per-cento-at-a-o-u-tm', + 'it_IT', + ]; + } + + /** + * @group Mage_Catalog + * @group Mage_Catalog_Model + */ + public function testGetSlugger(): void + { + $this->assertInstanceOf(AsciiSlugger::class, $this->subject->getSlugger()); + } + + /** + * @dataProvider provideGetSluggerConfig + * @group Mage_Catalog + * @group Mage_Catalog_Model + */ + public function testGetSluggerConfig($expectedResult, string $locale): void + { + $result = $this->subject->getSluggerConfig($locale); + + $this->assertArrayHasKey($locale, $result); + + $this->assertArrayHasKey('%', $result[$locale]); + $this->assertArrayHasKey('&', $result[$locale]); + + $this->assertSame($expectedResult[$locale]['%'], $result[$locale]['%']); + $this->assertSame($expectedResult[$locale]['&'], $result[$locale]['&']); + + $this->assertSame('at', $result[$locale]['@']); + } + + public function provideGetSluggerConfig(): Generator + { + yield 'de_DE' => [ + ['de_DE' => [ + '%' => 'prozent', + '&' => 'und', + ]], + 'de_DE', + ]; + yield 'en_US' => [ + ['en_US' => [ + '%' => 'percent', + '&' => 'and', + ]], + 'en_US', + ]; + yield 'fr_FR' => [ + ['fr_FR' => [ + '%' => 'pour cent', + '&' => 'et', + ]], + 'fr_FR', + ]; + } } diff --git a/tests/unit/Mage/Core/Helper/UnserializeArrayTest.php b/tests/unit/Mage/Core/Helper/UnserializeArrayTest.php new file mode 100644 index 00000000000..8803897b14a --- /dev/null +++ b/tests/unit/Mage/Core/Helper/UnserializeArrayTest.php @@ -0,0 +1,70 @@ +subject = Mage::helper('core/unserializeArray'); + } + + /** + * @dataProvider provideUnserialize + * @group Mage_Core + * @group Mage_Core_Helper + */ + public function testUnserialize($expectedTesult, $string): void + { + try { + $this->assertSame($expectedTesult, $this->subject->unserialize($string)); + } catch (Exception $exception) { + $this->assertSame($expectedTesult, $exception->getMessage()); + } + } + + public function provideUnserialize(): Generator + { + yield 'null' => [ + 'Error unserializing data.', + null, + ]; + yield 'empty string' => [ + 'Error unserializing data.', + '', + ]; + yield 'random string' => [ + 'unserialize(): Error at offset 0 of 3 bytes', + 'abc', + ]; + yield 'valid' => [ + ['key' => 'value'], + 'a:1:{s:3:"key";s:5:"value";}', + ]; + } +}