diff --git a/.phpstan.dist.baseline.neon b/.phpstan.dist.baseline.neon index 866199dca0c..679597bf50e 100644 --- a/.phpstan.dist.baseline.neon +++ b/.phpstan.dist.baseline.neon @@ -1056,12 +1056,6 @@ parameters: count: 1 path: app/code/core/Mage/Api2/Model/Resource/Validator/Eav.php - - - rawMessage: 'Call to an undefined method Zend_Validate_Interface::setMessage().' - identifier: method.notFound - count: 1 - path: app/code/core/Mage/Api2/Model/Resource/Validator/Fields.php - - rawMessage: 'Parameter #1 $request (Mage_Api2_Model_Request) of method Mage_Api2_Model_Route_Abstract::match() should be compatible with parameter $path (string) of method Zend_Controller_Router_Route::match()' identifier: method.childParameterType diff --git a/.phpstorm.meta.php/magento_helpers.meta.php b/.phpstorm.meta.php/magento_helpers.meta.php index d2b9be5ae8a..5971fb5ca06 100644 --- a/.phpstorm.meta.php/magento_helpers.meta.php +++ b/.phpstorm.meta.php/magento_helpers.meta.php @@ -103,6 +103,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, diff --git a/.phpstorm.meta.php/magento_helpers_methods.meta.php b/.phpstorm.meta.php/magento_helpers_methods.meta.php index bf3f63eecf4..e55d8b590b3 100644 --- a/.phpstorm.meta.php/magento_helpers_methods.meta.php +++ b/.phpstorm.meta.php/magento_helpers_methods.meta.php @@ -103,6 +103,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -302,6 +304,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -501,6 +505,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -700,6 +706,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -899,6 +907,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -1098,6 +1108,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -1297,6 +1309,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -1496,6 +1510,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -1695,6 +1711,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -1894,6 +1912,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -2093,6 +2113,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -2292,6 +2314,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -2491,6 +2515,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -2690,6 +2716,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -2889,6 +2917,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, @@ -3088,6 +3118,8 @@ 'core/unserializeArray' => \Mage_Core_Helper_UnserializeArray::class, 'core/url' => \Mage_Core_Helper_Url::class, 'core/url_rewrite' => \Mage_Core_Helper_Url_Rewrite::class, + 'core/validate' => \Mage_Core_Helper_Validate::class, + 'core/validate_abstract' => \Mage_Core_Helper_Validate_Abstract::class, 'csp' => \Mage_Csp_Helper_Data::class, 'csp/data' => \Mage_Csp_Helper_Data::class, 'currencysymbol' => \Mage_CurrencySymbol_Helper_Data::class, diff --git a/.phpunit.dist.xml b/.phpunit.dist.xml index dd00746b5ca..d886101339c 100644 --- a/.phpunit.dist.xml +++ b/.phpunit.dist.xml @@ -79,6 +79,9 @@ tests/unit/Mage/Newsletter + + tests/unit/Mage/Oauth + tests/unit/Mage/Page @@ -116,13 +119,12 @@ processUncoveredFiles="false" > + Mage.php app/code lib app/bootstrap.php - app/code/core/Mage/Admin/Model/Acl/Assert/Ip.php - app/code/core/Mage/Admin/Model/Acl/Assert/Time.php app/code/community app/code/local/ app/code/core/Mage/*/data diff --git a/app/code/core/Mage/Admin/Model/Block.php b/app/code/core/Mage/Admin/Model/Block.php index 3eb9840495e..868884f06b4 100644 --- a/app/code/core/Mage/Admin/Model/Block.php +++ b/app/code/core/Mage/Admin/Model/Block.php @@ -22,6 +22,8 @@ */ class Mage_Admin_Model_Block extends Mage_Core_Model_Abstract { + public const BLOCK_NAME_REGEX = '/^[-_a-zA-Z0-9]+\/[-_a-zA-Z0-9\/]+$/'; + /** * Initialize variable model */ @@ -32,35 +34,45 @@ protected function _construct() /** * @throws Exception - * @throws Zend_Validate_Exception * @return array|true */ public function validate() { - $errors = []; + $validator = $this->getValidationHelper(); + $violations = new ArrayObject(); - if (!Zend_Validate::is($this->getBlockName(), 'NotEmpty')) { - $errors[] = Mage::helper('adminhtml')->__('Block Name is required field.'); - } + $blockName = $this->getBlockName(); - $disallowedBlockNames = Mage::helper('admin/block')->getDisallowedBlockNames(); - if (in_array($this->getBlockName(), $disallowedBlockNames)) { - $errors[] = Mage::helper('adminhtml')->__('Block Name is disallowed.'); - } + $violations->append($validator->validateNotEmpty( + value: $blockName, + message: Mage::helper('adminhtml')->__('Block Name is required field.'), + )); - if (!Zend_Validate::is($this->getBlockName(), 'Regex', ['/^[-_a-zA-Z0-9]+\/[-_a-zA-Z0-9\/]+$/'])) { - $errors[] = Mage::helper('adminhtml')->__('Block Name is incorrect.'); - } + $violations->append($validator->validateChoice( + value: $blockName, + choices: Mage::helper('admin/block')->getDisallowedBlockNames(), + message: Mage::helper('adminhtml')->__('Block Name is disallowed.'), + match: false, + )); - if (!in_array($this->getIsAllowed(), ['0', '1'])) { - $errors[] = Mage::helper('adminhtml')->__('Is Allowed is required field.'); - } + $violations->append($validator->validateRegex( + value: $blockName, + pattern: self::BLOCK_NAME_REGEX, + message: Mage::helper('adminhtml')->__('Block Name is incorrect.'), + )); + + $violations->append($validator->validateChoice( + value: $this->getIsAllowed(), + choices: ['0', '1'], + message: Mage::helper('adminhtml')->__('Is Allowed is required field.'), + )); - if (empty($errors)) { + $errors = $validator->getErrorMessages($violations); + if (!$errors) { return true; } - return $errors; + return (array) $errors; } /** diff --git a/app/code/core/Mage/Admin/Model/Resource/User.php b/app/code/core/Mage/Admin/Model/Resource/User.php index 507dea94739..c8111e968c2 100644 --- a/app/code/core/Mage/Admin/Model/Resource/User.php +++ b/app/code/core/Mage/Admin/Model/Resource/User.php @@ -40,6 +40,7 @@ protected function _initUniqueFields() /** * Authenticate user by $username and $password * + * @throws Zend_Db_Adapter_Exception * @return $this */ public function recordLogin(Mage_Admin_Model_User $user) @@ -84,7 +85,7 @@ public function loadByUsername($username) /** * Check if user is assigned to any role * - * @param int|Mage_Admin_Model_User|Mage_Core_Model_Abstract $user + * @param int|Mage_Admin_Model_User|Mage_Core_Model_Abstract|string $user * @return null|array */ public function hasAssigned2Role($user) @@ -119,53 +120,57 @@ public function hasAssigned2Role($user) /** * Set created/modified values before user save * - * @param Mage_Admin_Model_User $user + * @param Mage_Admin_Model_User $object * @inheritDoc */ - protected function _beforeSave(Mage_Core_Model_Abstract $user) + protected function _beforeSave(Mage_Core_Model_Abstract $object) { - if ($user->isObjectNew()) { - $user->setCreated($this->formatDate(true)); + if ($object->isObjectNew()) { + $object->setCreated($this->formatDate(true)); } - $user->setModified($this->formatDate(true)); + $object->setModified($this->formatDate(true)); - return parent::_beforeSave($user); + return parent::_beforeSave($object); } /** * Unserialize user extra data after user save * + * @param Mage_Admin_Model_User $object * @return $this */ - protected function _afterSave(Mage_Core_Model_Abstract $user) + protected function _afterSave(Mage_Core_Model_Abstract $object) { - $this->_unserializeExtraData($user); + $this->_unserializeExtraData($object); return $this; } /** * Unserialize user extra data after user load * + * @param Mage_Admin_Model_User $object * @inheritDoc */ - protected function _afterLoad(Mage_Core_Model_Abstract $user) + protected function _afterLoad(Mage_Core_Model_Abstract $object) { - return parent::_afterLoad($this->_unserializeExtraData($user)); + return parent::_afterLoad($this->_unserializeExtraData($object)); } /** * Delete user role record with user * + * @param Mage_Admin_Model_User $object * @throws Exception + * @throws Throwable * @return $this */ - public function delete(Mage_Core_Model_Abstract $user) + public function delete(Mage_Core_Model_Abstract $object) { - $this->_beforeDelete($user); + $this->_beforeDelete($object); $adapter = $this->_getWriteAdapter(); - $uid = $user->getId(); + $uid = $object->getId(); $adapter->beginTransaction(); try { $conditions = [ @@ -180,16 +185,17 @@ public function delete(Mage_Core_Model_Abstract $user) throw $throwable; } - $this->_afterDelete($user); + $this->_afterDelete($object); return $this; } /** * TODO: unify _saveRelations() and add() methods, they make same things * + * @param Mage_Admin_Model_User $user * @throws Mage_Core_Exception * @throws Zend_Db_Adapter_Exception - * @return $this|Mage_Core_Model_Abstract + * @return $this|Mage_Admin_Model_User|Mage_Core_Model_Abstract */ public function _saveRelations(Mage_Core_Model_Abstract $user) { @@ -248,6 +254,7 @@ public function _saveRelations(Mage_Core_Model_Abstract $user) /** * Get user roles * + * @param Mage_Admin_Model_User $user * @return array */ public function getRoles(Mage_Core_Model_Abstract $user) @@ -262,10 +269,10 @@ public function getRoles(Mage_Core_Model_Abstract $user) ->from($table, []) ->joinLeft( ['ar' => $table], - "(ar.role_id = {$table}.parent_id and ar.role_type = 'G')", + "(ar.role_id = $table.parent_id and ar.role_type = 'G')", ['role_id'], ) - ->where("{$table}.user_id = :user_id"); + ->where("$table.user_id = :user_id"); $binds = [ 'user_id' => (int) $user->getId(), @@ -283,13 +290,16 @@ public function getRoles(Mage_Core_Model_Abstract $user) /** * Save user roles * + * @param Mage_Admin_Model_User $user + * @throws Zend_Db_Adapter_Exception + * @throws Zend_Cache_Exception * @return $this */ public function add(Mage_Core_Model_Abstract $user) { $dbh = $this->_getWriteAdapter(); $aRoles = $this->hasAssigned2Role($user); - if (count($aRoles)) { + if ($aRoles && count($aRoles)) { foreach ($aRoles as $data) { $dbh->delete( $this->getTable('admin/role'), @@ -327,6 +337,7 @@ public function add(Mage_Core_Model_Abstract $user) /** * Delete user role * + * @param Mage_Admin_Model_User $user * @return $this */ public function deleteFromRole(Mage_Core_Model_Abstract $user) @@ -353,6 +364,7 @@ public function deleteFromRole(Mage_Core_Model_Abstract $user) /** * Check if role user exists * + * @param Mage_Admin_Model_User $user * @return array */ public function roleUserExists(Mage_Core_Model_Abstract $user) @@ -380,6 +392,7 @@ public function roleUserExists(Mage_Core_Model_Abstract $user) /** * Check if user exists * + * @param Mage_Admin_Model_User $user * @return array|false */ public function userExists(Mage_Core_Model_Abstract $user) @@ -403,8 +416,9 @@ public function userExists(Mage_Core_Model_Abstract $user) /** * Save user extra data * - * @param Mage_Core_Model_Abstract $object + * @param Mage_Admin_Model_User $object * @param string $data + * @throws Zend_Db_Adapter_Exception * @return $this */ public function saveExtra($object, $data) @@ -423,8 +437,10 @@ public function saveExtra($object, $data) /** * Set reload ACL flag * - * @param Mage_Core_Model_Abstract $object + * @param Mage_Admin_Model_User $object * @param int $flag + * @throws Zend_Cache_Exception + * @throws Zend_Db_Adapter_Exception * @return $this */ public function saveReloadAclFlag($object, $flag) @@ -450,6 +466,7 @@ public function saveReloadAclFlag($object, $flag) /** * Unserializes user extra data * + * @param Mage_Admin_Model_User $user * @return Mage_Core_Model_Abstract */ protected function _unserializeExtraData(Mage_Core_Model_Abstract $user) diff --git a/app/code/core/Mage/Admin/Model/User.php b/app/code/core/Mage/Admin/Model/User.php index 07257270e62..f2dee576b41 100644 --- a/app/code/core/Mage/Admin/Model/User.php +++ b/app/code/core/Mage/Admin/Model/User.php @@ -7,6 +7,8 @@ * @package Mage_Admin */ +use Symfony\Component\Validator\ConstraintViolationListInterface; + /** * Admin user model * @@ -40,7 +42,7 @@ * @method $this setIsActive(int $value) * @method array getExtra() * @method $this setExtra(string $value) - * @method int getUserId() + * @method null|int getUserId() * @method int getRoleId() * @method bool hasNewPassword() * @method string getNewPassword() @@ -188,6 +190,7 @@ protected function getSession() * Save admin user extra data (like configuration sections state) * * @param array|string $data + * @throws Zend_Db_Adapter_Exception * @return $this */ public function saveExtra($data) @@ -345,7 +348,7 @@ public function getName($separator = ' ') /** * Retrieve user identifier * - * @return mixed + * @return int */ public function getId() { @@ -416,6 +419,9 @@ public function authenticate($username, $password) return $result; } + /** + * @throws Exception + */ public function validatePasswordHash(string $string1, string $string2): bool { return Mage::helper('core')->validateHash($string1, $string2); @@ -427,7 +433,8 @@ public function validatePasswordHash(string $string1, string $string2): bool * @param string $username * @param string $password * @throws Mage_Core_Exception - * @return $this + * @throws Zend_Db_Adapter_Exception + * @return $this */ public function login($username, $password) { @@ -529,7 +536,7 @@ public function findFirstAvailableMenu($parent = null, $path = '', $level = 0) return (string) $child->action; } elseif ($child->children) { $action = $this->findFirstAvailableMenu($child->children, $path . $childName . '/', $level + 1); - return $action ? $action : (string) $child->action; + return $action ?: (string) $child->action; } } } @@ -554,6 +561,7 @@ public function hasAvailableResources() * @deprecated Please use getStartupPageUrl() method instead * @see getStartupPageUrl() * @return string + * @codeCoverageIgnore */ public function getStatrupPageUrl() { @@ -584,27 +592,38 @@ public function getStartupPageUrl() * Validate user attribute values. * Returns TRUE or array of errors. * - * @throws Zend_Validate_Exception * @return array|true */ public function validate() { - $errors = new ArrayObject(); - - if (!Zend_Validate::is($this->getUsername(), 'NotEmpty')) { - $errors->append(Mage::helper('adminhtml')->__('User Name is required field.')); - } - - if (!Zend_Validate::is($this->getFirstname(), 'NotEmpty')) { - $errors->append(Mage::helper('adminhtml')->__('First Name is required field.')); - } - - if (!Zend_Validate::is($this->getLastname(), 'NotEmpty')) { - $errors->append(Mage::helper('adminhtml')->__('Last Name is required field.')); - } - - if (!Zend_Validate::is($this->getEmail(), 'EmailAddress')) { - $errors->append(Mage::helper('adminhtml')->__('Please enter a valid email.')); + $validator = $this->getValidationHelper(); + $violations = new ArrayObject(); + $errors = new ArrayObject(); + + $violations->append($validator->validateNotEmpty( + value: $this->getUsername(), + message: Mage::helper('adminhtml')->__('User Name is required field.'), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getFirstname(), + message: Mage::helper('adminhtml')->__('First Name is required field.'), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getLastname(), + message: Mage::helper('adminhtml')->__('Last Name is required field.'), + )); + + $violations->append($validator->validateEmail( + value: $this->getEmail(), + message: Mage::helper('adminhtml')->__('Please enter a valid email.'), + )); + + foreach ($violations as $violation) { + foreach ($violation as $error) { + $errors->append($error->getMessage()); + } } if ($this->hasNewPassword()) { @@ -614,19 +633,21 @@ public function validate() } if (isset($password)) { - $minAdminPasswordLength = $this->getMinAdminPasswordLength(); - if (Mage::helper('core/string')->strlen($password) < $minAdminPasswordLength) { - $errors->append(Mage::helper('adminhtml') - ->__('Password must be at least of %d characters.', $minAdminPasswordLength)); + $violations = new ArrayObject(); + $violations->append($this->getPasswordValidator(value: $password)); + + if ($this->hasPasswordConfirmation()) { + $violations->append($validator->validateIdentical( + value: $this->getPasswordConfirmation(), + compare: $password, + message: Mage::helper('adminhtml')->__('Password confirmation must be same as password.'), + )); } - if (!preg_match('/[a-z]/iu', $password) || !preg_match('/[0-9]/u', $password)) { - $errors->append(Mage::helper('adminhtml') - ->__('Password must include both numeric and alphabetic characters.')); - } - - if ($this->hasPasswordConfirmation() && $password != $this->getPasswordConfirmation()) { - $errors->append(Mage::helper('adminhtml')->__('Password confirmation must be same as password.')); + foreach ($violations as $violation) { + foreach ($violation as $error) { + $errors->append($error->getMessage()); + } } Mage::dispatchEvent('admin_user_validate', [ @@ -646,19 +667,33 @@ public function validate() return (array) $errors; } + public function getPasswordValidator(mixed $value): ConstraintViolationListInterface + { + $min = $this->getMinAdminPasswordLength(); + $validator = $this->getValidationHelper(); + + return $validator->validatePassword( + value: $value, + min: $min, + minMessage: Mage::helper('adminhtml')->__('Password must be at least of %d characters.', $min), + regexMessage: Mage::helper('adminhtml')->__('Password must include both numeric and alphabetic characters.'), + ); + } + /** * Validate password against current user password - * Returns true or array of errors. + * Returns TRUE or array of errors. * * @param string $password - * @throws Zend_Validate_Exception + * @throws Exception * @return array|true */ public function validateCurrentPassword($password) { - $result = []; + $validator = $this->getValidationHelper(); + $result = []; - if (!Zend_Validate::is($password, 'NotEmpty')) { + if ($validator->validateNotEmpty($password)->count() > 0) { $result[] = $this->_getHelper('adminhtml')->__('Current password field cannot be empty.'); } elseif (is_null($this->getId()) || !Mage::helper('core')->validateHash($password, $this->getPassword())) { $result[] = $this->_getHelper('adminhtml')->__('Invalid current password.'); diff --git a/app/code/core/Mage/Admin/Model/Variable.php b/app/code/core/Mage/Admin/Model/Variable.php index ea203c87eb2..10beeec1504 100644 --- a/app/code/core/Mage/Admin/Model/Variable.php +++ b/app/code/core/Mage/Admin/Model/Variable.php @@ -32,30 +32,38 @@ protected function _construct() /** * @throws Exception - * @throws Zend_Validate_Exception - * @return array|bool + * @return array|true */ public function validate() { - $errors = []; + $validator = $this->getValidationHelper(); + $violations = new ArrayObject(); - if (!Zend_Validate::is($this->getVariableName(), 'NotEmpty')) { - $errors[] = Mage::helper('adminhtml')->__('Variable Name is required field.'); - } + $variableName = $this->getVariableName(); - if (!Zend_Validate::is($this->getVariableName(), 'Regex', ['/^[-_a-zA-Z0-9\/]*$/'])) { - $errors[] = Mage::helper('adminhtml')->__('Variable Name is incorrect.'); - } + $violations->append($validator->validateNotEmpty( + value: $variableName, + message: Mage::helper('adminhtml')->__('Variable Name is required field.'), + )); - if (!in_array($this->getIsAllowed(), ['0', '1'])) { - $errors[] = Mage::helper('adminhtml')->__('Is Allowed is required field.'); - } + $violations->append($validator->validateRegex( + value: $variableName, + pattern: '/^[-_a-zA-Z0-9\/]*$/', + message: Mage::helper('adminhtml')->__('Variable Name is incorrect.'), + )); + + $violations->append($validator->validateChoice( + value: $this->getIsAllowed(), + choices: ['0', '1'], + message: Mage::helper('adminhtml')->__('Is Allowed is required field.'), + )); - if (empty($errors)) { + $errors = $validator->getErrorMessages($violations); + if (!$errors) { return true; } - return $errors; + return (array) $errors; } /** diff --git a/app/code/core/Mage/Adminhtml/Model/Email/PathValidator.php b/app/code/core/Mage/Adminhtml/Model/Email/PathValidator.php index 9d3269277b2..21988748733 100644 --- a/app/code/core/Mage/Adminhtml/Model/Email/PathValidator.php +++ b/app/code/core/Mage/Adminhtml/Model/Email/PathValidator.php @@ -12,7 +12,7 @@ * * @package Mage_Adminhtml */ -class Mage_Adminhtml_Model_Email_PathValidator extends Zend_Validate_Abstract +class Mage_Adminhtml_Model_Email_PathValidator { /** * Returns true if and only if $value meets the validation requirements diff --git a/app/code/core/Mage/Adminhtml/Model/LayoutUpdate/Validator.php b/app/code/core/Mage/Adminhtml/Model/LayoutUpdate/Validator.php index ce20d3750b8..b8ee3a43718 100644 --- a/app/code/core/Mage/Adminhtml/Model/LayoutUpdate/Validator.php +++ b/app/code/core/Mage/Adminhtml/Model/LayoutUpdate/Validator.php @@ -14,7 +14,7 @@ * * @package Mage_Adminhtml */ -class Mage_Adminhtml_Model_LayoutUpdate_Validator extends Zend_Validate_Abstract +class Mage_Adminhtml_Model_LayoutUpdate_Validator extends Mage_Core_Helper_Validate_Abstract { public const XML_INVALID = 'invalidXml'; diff --git a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Email/Address.php b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Email/Address.php index 0093d143398..979902f4f45 100644 --- a/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Email/Address.php +++ b/app/code/core/Mage/Adminhtml/Model/System/Config/Backend/Email/Address.php @@ -14,11 +14,16 @@ */ class Mage_Adminhtml_Model_System_Config_Backend_Email_Address extends Mage_Core_Model_Config_Data { + /** + * @throws Mage_Core_Exception + */ protected function _beforeSave() { - $value = $this->getValue(); - if (!Zend_Validate::is($value, 'EmailAddress')) { - Mage::throwException(Mage::helper('adminhtml')->__('Invalid email address "%s".', $value)); + $email = $this->getValue(); + + $validator = $this->getValidationHelper(); + if ($validator->validateEmail(value: $email)->count() > 0) { + Mage::throwException(Mage::helper('adminhtml')->__('Invalid email address "%s".', $email)); } return $this; diff --git a/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php b/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php index 951845b27e3..231898cede8 100644 --- a/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php +++ b/app/code/core/Mage/Adminhtml/controllers/Catalog/Product/AttributeController.php @@ -201,8 +201,12 @@ public function saveAction() //validate attribute_code if (isset($data['attribute_code'])) { - $validatorAttrCode = new Zend_Validate_Regex(['pattern' => '/^(?!event$)[a-z][a-z_0-9]{1,254}$/']); - if (!$validatorAttrCode->isValid($data['attribute_code'])) { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if ($validator->validateRegex( + value: $data['attribute_code'], + pattern: '/^(?!event$)[a-z][a-z_0-9]{1,254}$/', + )->count() > 0) { $session->addError( Mage::helper('catalog')->__('Attribute code is invalid. Please use only letters (a-z), numbers (0-9) or underscore(_) in this field, first character should be a letter. Do not use "event" for an attribute code.'), ); diff --git a/app/code/core/Mage/Adminhtml/controllers/IndexController.php b/app/code/core/Mage/Adminhtml/controllers/IndexController.php index e73a3609b70..b47b2a6b169 100644 --- a/app/code/core/Mage/Adminhtml/controllers/IndexController.php +++ b/app/code/core/Mage/Adminhtml/controllers/IndexController.php @@ -205,6 +205,8 @@ protected function _getDeniedIframe() /** * Forgot administrator password action + * @throws Mage_Core_Exception + * @throws Throwable */ public function forgotpasswordAction() { @@ -216,7 +218,9 @@ public function forgotpasswordAction() if ($this->_validateFormKey()) { if (!empty($email)) { // Validate received data to be an email address - if (Zend_Validate::is($email, 'EmailAddress')) { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if ($validator->validateEmail(value: $email)->count() === 0) { $collection = Mage::getResourceModel('admin/user_collection'); /** @var Mage_Admin_Model_Resource_User_Collection $collection */ $collection->addFieldToFilter('email', $email); diff --git a/app/code/core/Mage/Api/Model/User.php b/app/code/core/Mage/Api/Model/User.php index 9521c6c89ce..0aca07828aa 100644 --- a/app/code/core/Mage/Api/Model/User.php +++ b/app/code/core/Mage/Api/Model/User.php @@ -358,28 +358,33 @@ protected function _getHelper($helperName) /** * Validate user attribute values. * - * @throws Zend_Validate_Exception * @return array|true */ public function validate() { - $errors = new ArrayObject(); - - if (!Zend_Validate::is($this->getUsername(), 'NotEmpty')) { - $errors->append($this->_getHelper('api')->__('User Name is required field.')); - } - - if (!Zend_Validate::is($this->getFirstname(), 'NotEmpty')) { - $errors->append($this->_getHelper('api')->__('First Name is required field.')); - } - - if (!Zend_Validate::is($this->getLastname(), 'NotEmpty')) { - $errors->append($this->_getHelper('api')->__('Last Name is required field.')); - } - - if (!Zend_Validate::is($this->getEmail(), 'EmailAddress')) { - $errors->append($this->_getHelper('api')->__('Please enter a valid email.')); - } + $validator = $this->getValidationHelper(); + $violations = new ArrayObject(); + $errors = new ArrayObject(); + + $violations->append($validator->validateNotEmpty( + value: $this->getUsername(), + message: Mage::helper('api')->__('User Name is required field.'), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getFirstname(), + message: Mage::helper('api')->__('First Name is required field.'), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getLastname(), + message: Mage::helper('api')->__('Last Name is required field.'), + )); + + $violations->append($validator->validateEmail( + value: $this->getEmail(), + message: Mage::helper('adminhtml')->__('Please enter a valid email.'), + )); if ($this->hasNewApiKey()) { $apiKey = $this->getNewApiKey(); @@ -389,18 +394,39 @@ public function validate() if (isset($apiKey)) { $minCustomerPasswordLength = $this->_getMinCustomerPasswordLength(); - if (strlen($apiKey) < $minCustomerPasswordLength) { - $errors->append($this->_getHelper('api') - ->__('Api Key must be at least of %d characters.', $minCustomerPasswordLength)); - } - - if (!preg_match('/[a-z]/iu', $apiKey) || !preg_match('/[0-9]/u', $apiKey)) { - $errors->append($this->_getHelper('api') - ->__('Api Key must include both numeric and alphabetic characters.')); + $violations->append($validator->validateLength( + value: $apiKey, + min: $minCustomerPasswordLength, + minMessage: $this->_getHelper('api') + ->__('Api Key must be at least of %d characters.', $minCustomerPasswordLength), + )); + + $violations->append($validator->validateRegex( + value: $apiKey, + pattern: '/[a-z]/iu', + message: $this->_getHelper('api') + ->__('Api Key must include both numeric and alphabetic characters.'), + )); + + $violations->append($validator->validateRegex( + value: $apiKey, + pattern: '/[0-9]/u', + message: $this->_getHelper('api') + ->__('Api Key must include both numeric and alphabetic characters.'), + )); + + if ($this->hasApiKeyConfirmation()) { + $violations->append($validator->validateIdentical( + value: $apiKey, + compare: $this->getApiKeyConfirmation(), + message: $this->_getHelper('api')->__('Api Key confirmation must be same as Api Key.'), + )); } + } - if ($this->hasApiKeyConfirmation() && $apiKey != $this->getApiKeyConfirmation()) { - $errors->append($this->_getHelper('api')->__('Api Key confirmation must be same as Api Key.')); + foreach ($violations as $violation) { + foreach ($violation as $error) { + $errors->append($error->getMessage()); } } diff --git a/app/code/core/Mage/Api2/Model/Resource/Validator/Fields.php b/app/code/core/Mage/Api2/Model/Resource/Validator/Fields.php index 9d5350e3848..43ba1c33dc3 100644 --- a/app/code/core/Mage/Api2/Model/Resource/Validator/Fields.php +++ b/app/code/core/Mage/Api2/Model/Resource/Validator/Fields.php @@ -7,6 +7,8 @@ * @package Mage_Api2 */ +use Symfony\Component\Validator\Constraint; + /** * API2 Fields Validator * @@ -27,10 +29,10 @@ class Mage_Api2_Model_Resource_Validator_Fields extends Mage_Api2_Model_Resource protected $_resource; /** - * List of Validators (Zend_Validate_Interface) + * List of Validators (Constraint[]) * The key is a field name, a value is validator for this field * - * @var array + * @var array */ protected $_validators; @@ -72,14 +74,16 @@ public function __construct($options) /** * Build validator chain with config data * - * @throws Exception If validator type is not set - * @throws Exception If validator is not exist + * @throws Mage_Core_Exception If validator type is not set */ protected function _buildValidatorsChain(array $validationConfig) { + $validator = $this->getValidationHelper(); + foreach ($validationConfig as $field => $validatorsConfig) { + $field = (string) $field; if (count($validatorsConfig)) { - $chainForOneField = new Zend_Validate(); + $chainForOneField = new ArrayObject(); foreach ($validatorsConfig as $validatorName => $validatorConfig) { // it is required field if ($validatorName == 'required' && $validatorConfig == 1) { @@ -89,20 +93,19 @@ protected function _buildValidatorsChain(array $validationConfig) // instantiation of the validator class if (!isset($validatorConfig['type'])) { - throw new Exception("Validator type is not set for {$validatorName}"); + throw new Mage_Core_Exception("Validator type is not set for $validatorName"); } - $validator = $this->_getValidatorInstance( - $validatorConfig['type'], - !empty($validatorConfig['options']) ? $validatorConfig['options'] : [], - ); + $options = $validatorConfig['options'] ?? []; + // set custom message if (isset($validatorConfig['message'])) { - $validator->setMessage($validatorConfig['message']); + $options['message'] = $validatorConfig['message']; } // add to list of validators - $chainForOneField->addValidator($validator); + $constraints = $validator->getContraintsByType(type: $validatorConfig['type'], options: $options); + $chainForOneField->append($constraints); } $this->_validators[$field] = $chainForOneField; @@ -110,25 +113,6 @@ protected function _buildValidatorsChain(array $validationConfig) } } - /** - * Get validator object instance - * Override the method if we need to use not only Zend validators! - * - * @param string $type - * @param array $options - * @throws Exception If validator is not exist - * @return Zend_Validate_Interface - */ - protected function _getValidatorInstance($type, $options) - { - $validatorClass = 'Zend_Validate_' . $type; - if (!class_exists($validatorClass)) { - throw new Exception("Validator {$type} is not exist"); - } - - return new $validatorClass($options); - } - /** * Validate data. * If fails validation, then this method returns false, and @@ -140,16 +124,18 @@ protected function _getValidatorInstance($type, $options) */ public function isValidData(array $data, $isPartial = false) { + $validator = $this->getValidationHelper(); + $isValid = true; // required fields if (!$isPartial && count($this->_requiredFields) > 0) { - $notEmptyValidator = new Zend_Validate_NotEmpty(); foreach ($this->_requiredFields as $requiredField) { - if (!$notEmptyValidator->isValid($data[$requiredField] ?? null)) { + $violations = $validator->validateNotEmpty(value: $data[$requiredField] ?? null); + if ($violations->count() > 0) { $isValid = false; - foreach ($notEmptyValidator->getMessages() as $message) { - $this->_addError(sprintf('%s: %s', $requiredField, $message)); + foreach ($violations as $violation) { + $this->_addError(sprintf('%s: %s', $requiredField, $violation->getMessage())); } } } @@ -158,12 +144,17 @@ public function isValidData(array $data, $isPartial = false) // fields rules foreach ($data as $field => $value) { if (isset($this->_validators[$field])) { - /** @var Zend_Validate_Interface $validator */ - $validator = $this->_validators[$field]; - if (!$validator->isValid($value)) { - $isValid = false; - foreach ($validator->getMessages() as $message) { - $this->_addError(sprintf('%s: %s', $field, $message)); + /** @var ArrayObject $chainForOneField */ + foreach ($this->_validators[$field] as $chainForOneField) { + /** @var Constraint[] $constraints */ + foreach ($chainForOneField as $constraints) { + $violations = $validator->validate(value: $value, constraints: $constraints); + if ($violations->count() > 0) { + $isValid = false; + foreach ($violations as $violation) { + $this->_addError(sprintf('%s: %s', $field, $violation->getMessage())); + } + } } } } @@ -171,4 +162,11 @@ public function isValidData(array $data, $isPartial = false) return $isValid; } + + protected function getValidationHelper(): Mage_Core_Helper_Validate + { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + return $validator; + } } diff --git a/app/code/core/Mage/Catalog/Model/Api2/Product/Validator/Product.php b/app/code/core/Mage/Catalog/Model/Api2/Product/Validator/Product.php index cd4d16368a4..a5eae644476 100644 --- a/app/code/core/Mage/Catalog/Model/Api2/Product/Validator/Product.php +++ b/app/code/core/Mage/Catalog/Model/Api2/Product/Validator/Product.php @@ -115,20 +115,27 @@ public function isValidData(array $data) * * @param array $data * @param Mage_Eav_Model_Entity_Type $productEntity + * @throws Mage_Api2_Exception */ protected function _validateAttributes($data, $productEntity) { - if (!isset($data['attribute_set_id']) || empty($data['attribute_set_id'])) { + if (empty($data['attribute_set_id'])) { $this->_critical('Missing "attribute_set_id" in request.', Mage_Api2_Model_Server::HTTP_BAD_REQUEST); } - if (!isset($data['type_id']) || empty($data['type_id'])) { + if (empty($data['type_id'])) { $this->_critical('Missing "type_id" in request.', Mage_Api2_Model_Server::HTTP_BAD_REQUEST); } // Validate weight - if (isset($data['weight']) && !empty($data['weight']) && $data['weight'] > 0 - && !Zend_Validate::is($data['weight'], 'Between', [0, self::MAX_DECIMAL_VALUE]) + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if (!empty($data['weight']) && $data['weight'] > 0 + && $validator->validateRange( + value: $data['weight'], + min: 0, + max: self::MAX_DECIMAL_VALUE, + )->count() > 0 ) { $this->_addError('The "weight" value is not within the specified range.'); } @@ -253,6 +260,7 @@ protected function _validateProductType($data) * * @param array $data * @param Mage_Eav_Model_Entity_Type $productEntity + * @throws Mage_Api2_Exception * @return true|void */ protected function _validateAttributeSet($data, $productEntity) @@ -284,8 +292,12 @@ protected function _validateSku($data) return true; } - if (!Zend_Validate::is((string) $data['sku'], 'StringLength', ['min' => 0, 'max' => 64])) { - $this->_addError('SKU length should be 64 characters maximum.'); + $skuMaxLength = Mage_Catalog_Model_Product_Attribute_Backend_Sku::SKU_MAX_LENGTH; + + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if ($validator->validateLength(value: $data['sku'], max: $skuMaxLength)->count() > 0) { + $this->_addError(sprintf('SKU length should be %d characters maximum.', $skuMaxLength)); } } diff --git a/app/code/core/Mage/Catalog/Model/Product/Option/Type/Default.php b/app/code/core/Mage/Catalog/Model/Product/Option/Type/Default.php index 5bf9ea49818..2e8020f05b2 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Option/Type/Default.php +++ b/app/code/core/Mage/Catalog/Model/Product/Option/Type/Default.php @@ -20,7 +20,7 @@ * @method $this setQuoteItem(Mage_Sales_Model_Quote_Item $value) * @method array|int getUserValue() * @method $this setRequest(Varien_Object $value) - * @method $this setUserValue(array|int $value) + * @method $this setUserValue(null|array|int $value) */ class Mage_Catalog_Model_Product_Option_Type_Default extends Varien_Object { diff --git a/app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php b/app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php index 302541d7613..30b04f3ced1 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php +++ b/app/code/core/Mage/Catalog/Model/Product/Option/Type/File.php @@ -16,6 +16,16 @@ */ class Mage_Catalog_Model_Product_Option_Type_File extends Mage_Catalog_Model_Product_Option_Type_Default { + public const ERROR_EXCLUDE_EXTENSION_FALSE_EXTENSION = 'fileExcludeExtensionFalse'; + + public const ERROR_EXTENSION_FALSE_EXTENSION = 'fileExtensionFalse'; + + public const ERROR_IMAGESIZE_WIDTH_TOO_BIG = 'fileImageSizeWidthTooBig'; + + public const ERROR_IMAGESIZE_HEIGHT_TOO_BIG = 'fileImageSizeHeightTooBig'; + + public const ERROR_FILESIZE_TOO_BIG = 'fileFilesSizeTooBig'; + /** * Url for custom option download controller * @var string @@ -104,7 +114,7 @@ protected function _getCurrentConfigFileInfo() * Validate user input for option * * @param array $values All product option values, i.e. array (option_id => mixed, option_id => mixed...) - * @throws Mage_Core_Exception|Zend_Validate_Exception + * @throws Mage_Core_Exception * @return $this */ public function validateUserValue($values) @@ -112,7 +122,6 @@ public function validateUserValue($values) Mage::getSingleton('checkout/session')->setUseNotice(false); $this->setIsValid(true); - $option = $this->getOption(); /* * Check whether we receive uploaded file or restore file by: reorder/edit configuration or @@ -300,7 +309,6 @@ protected function _validateUploadedFile() * * @param array $optionValue * @throws Mage_Core_Exception - * @throws Zend_Validate_Exception * @return bool */ protected function _validateFile($optionValue) @@ -337,7 +345,9 @@ protected function _validateFile($optionValue) return false; } - $validatorChain = new Zend_Validate(); + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + $validatorChain = new ArrayObject(); $_dimentions = []; @@ -354,49 +364,75 @@ protected function _validateFile($optionValue) } if ($_dimentions !== []) { - $validatorChain->addValidator( - new Zend_Validate_File_ImageSize($_dimentions), - ); + $validatorChain->append($validator->validateImage( + value: $fileFullPath, + maxWidth: $_dimentions['maxwidth'] ?? null, + maxHeight: $_dimentions['maxheight'] ?? null, + maxWidthMessage: Mage::helper('catalog')->__( + $this->getValidatorMessage(self::ERROR_IMAGESIZE_WIDTH_TOO_BIG), + $option->getTitle(), + $option->getImageSizeX(), + $option->getImageSizeY(), + ), + maxHeightMessage: Mage::helper('catalog')->__( + $this->getValidatorMessage(self::ERROR_IMAGESIZE_HEIGHT_TOO_BIG), + $option->getTitle(), + $option->getImageSizeX(), + $option->getImageSizeY(), + ), + )); } + // Maximum filesize // File extension $_allowed = $this->_parseExtensionsString($option->getFileExtension()); if ($_allowed !== null) { - $validatorChain->addValidator(new Zend_Validate_File_Extension($_allowed)); + $validatorChain->append($validator->validateFile( + value: $fileFullPath, + maxSize: $this->_getUploadMaxFilesize(), + maxSizeMessage: Mage::helper('catalog')->__( + "The file '%s' you uploaded is larger than %s Megabytes allowed by server", + $optionValue['title'], + $this->_bytesToMbytes($this->_getUploadMaxFilesize()), + ), + extensions: $_allowed, + extensionsMessage: Mage::helper('catalog')->__( + $this->getValidatorMessage(self::ERROR_EXTENSION_FALSE_EXTENSION), + $optionValue['title'], + $option->getTitle(), + ), + )); } else { $_forbidden = $this->_parseExtensionsString($this->getConfigData('forbidden_extensions')); if ($_forbidden !== null) { - $validatorChain->addValidator(new Zend_Validate_File_ExcludeExtension($_forbidden)); + $validatorChain->append($validator->validateChoice( + value: $_allowed, + choices: $_forbidden, + multiple: true, + message: Mage::helper('catalog')->__( + $this->getValidatorMessage(self::ERROR_EXTENSION_FALSE_EXTENSION), + $optionValue['title'], + $option->getTitle(), + ), + match: false, + )); } } - // Maximum filesize - $validatorChain->addValidator( - new Zend_Validate_File_FilesSize(['max' => $this->_getUploadMaxFilesize()]), - ); - - if ($validatorChain->isValid($fileFullPath)) { + $errors = $validator->getErrorMessages($validatorChain); + if (!$errors) { return is_readable($fileFullPath) && isset($optionValue['secret_key']) && substr(md5(file_get_contents($fileFullPath)), 0, 20) == $optionValue['secret_key']; - } elseif ($validatorChain->getErrors()) { - $errors = $this->_getValidatorErrors($validatorChain->getErrors(), $optionValue); - - if (count($errors) > 0) { - $this->setIsValid(false); - Mage::throwException(implode("\n", $errors)); - } } else { $this->setIsValid(false); - Mage::throwException(Mage::helper('catalog')->__('Please specify the product required option(s)')); + Mage::throwException(implode("\n", iterator_to_array($errors))); } - - return false; } /** * Get Error messages for validator Errors - * @param array $errors Array of validation failure message codes @see Zend_Validate::getErrors() + * @param array $errors Array of validation failure message codes * @param array $fileInfo File info * @throws Mage_Core_Exception * @return array Array of error messages @@ -406,27 +442,52 @@ protected function _getValidatorErrors($errors, $fileInfo) $option = $this->getOption(); $result = []; foreach ($errors as $errorCode) { - if ($errorCode == Zend_Validate_File_ExcludeExtension::FALSE_EXTENSION) { - $result[] = Mage::helper('catalog')->__("The file '%s' for '%s' has an invalid extension", $fileInfo['title'], $option->getTitle()); - } elseif ($errorCode == Zend_Validate_File_Extension::FALSE_EXTENSION) { - $result[] = Mage::helper('catalog')->__("The file '%s' for '%s' has an invalid extension", $fileInfo['title'], $option->getTitle()); - } elseif ($errorCode == Zend_Validate_File_ImageSize::WIDTH_TOO_BIG - || $errorCode == Zend_Validate_File_ImageSize::HEIGHT_TOO_BIG - ) { - $result[] = Mage::helper('catalog')->__("Maximum allowed image size for '%s' is %sx%s px.", $option->getTitle(), $option->getImageSizeX(), $option->getImageSizeY()); - } elseif ($errorCode == Zend_Validate_File_FilesSize::TOO_BIG) { - $result[] = Mage::helper('catalog')->__("The file '%s' you uploaded is larger than %s Megabytes allowed by server", $fileInfo['title'], $this->_bytesToMbytes($this->_getUploadMaxFilesize())); + if (in_array($errorCode, [self::ERROR_EXCLUDE_EXTENSION_FALSE_EXTENSION, self::ERROR_EXTENSION_FALSE_EXTENSION])) { + $result[] = Mage::helper('catalog')->__( + $this->getValidatorMessage($errorCode), + $fileInfo['title'], + $option->getTitle(), + ); + } elseif (in_array($errorCode, [self::ERROR_IMAGESIZE_HEIGHT_TOO_BIG, self::ERROR_IMAGESIZE_WIDTH_TOO_BIG])) { + $result[] = Mage::helper('catalog')->__( + $this->getValidatorMessage($errorCode), + $option->getTitle(), + $option->getImageSizeX(), + $option->getImageSizeY(), + ); + } elseif ($errorCode === self::ERROR_FILESIZE_TOO_BIG) { + $result[] = Mage::helper('catalog')->__( + $this->getValidatorMessage($errorCode), + $fileInfo['title'], + $this->_bytesToMbytes($this->_getUploadMaxFilesize()), + ); } } return $result; } + /** + * @param self::ERROR_* $errorCode + */ + protected function getValidatorMessage(string $errorCode): string + { + $messages = [ + self::ERROR_EXCLUDE_EXTENSION_FALSE_EXTENSION => "The file '%s' for '%s' has an invalid extension", + self::ERROR_EXTENSION_FALSE_EXTENSION => "The file '%s' for '%s' has an invalid extension", + self::ERROR_FILESIZE_TOO_BIG => "The file '%s' you uploaded is larger than %s Megabytes allowed by server", + self::ERROR_IMAGESIZE_HEIGHT_TOO_BIG => "Maximum allowed image size for '%s' is %sx%s px.", + self::ERROR_IMAGESIZE_WIDTH_TOO_BIG => "Maximum allowed image size for '%s' is %sx%s px.", + ]; + + return $messages[$errorCode] ?? ''; + } + /** * Prepare option value for cart * * @throws Mage_Core_Exception - * @return mixed Prepared option value + * @return null|string Prepared option value */ public function prepareForCart() { diff --git a/app/code/core/Mage/Checkout/Model/Type/Onepage.php b/app/code/core/Mage/Checkout/Model/Type/Onepage.php index c259c7ff583..7d51930ab2f 100644 --- a/app/code/core/Mage/Checkout/Model/Type/Onepage.php +++ b/app/code/core/Mage/Checkout/Model/Type/Onepage.php @@ -510,7 +510,9 @@ protected function _processValidateCustomer(Mage_Sales_Model_Quote_Address $addr } } elseif (self::METHOD_GUEST == $this->getQuote()->getCheckoutMethod()) { $email = $address->getData('email'); - if (!Zend_Validate::is($email, 'EmailAddress')) { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if ($validator->validateEmail($email)->count() > 0) { return [ 'error' => -1, 'message' => Mage::helper('checkout')->__('Invalid email address "%s"', $email), diff --git a/app/code/core/Mage/Contacts/controllers/IndexController.php b/app/code/core/Mage/Contacts/controllers/IndexController.php index e45000d1b2f..2634a36337d 100644 --- a/app/code/core/Mage/Contacts/controllers/IndexController.php +++ b/app/code/core/Mage/Contacts/controllers/IndexController.php @@ -71,17 +71,16 @@ public function postAction() $postObject = new Varien_Object(); $postObject->setData($post); - // check data - $error = false; - if (!Zend_Validate::is(trim($post['name']), 'NotEmpty')) { - $error = true; - } elseif (!Zend_Validate::is(trim($post['comment']), 'NotEmpty')) { - $error = true; - } elseif (!Zend_Validate::is(trim($post['email']), 'EmailAddress')) { - $error = true; - } + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + $violations = new ArrayObject(); + + $violations->append($validator->validateNotEmpty(value: trim($post['name']))); + $violations->append($validator->validateNotEmpty(value: trim($post['comment']))); + $violations->append($validator->validateEmail(value: trim($post['email']))); - if ($error) { + $errors = $validator->getErrorMessages($violations); + if ($errors) { Mage::throwException($this->__('Unable to submit your request. Please, try again later')); } diff --git a/app/code/core/Mage/Core/Helper/Validate.php b/app/code/core/Mage/Core/Helper/Validate.php new file mode 100644 index 00000000000..02153165036 --- /dev/null +++ b/app/code/core/Mage/Core/Helper/Validate.php @@ -0,0 +1,694 @@ +validate($value, $constraints, $groups)->count() === 0; + } + + /** + * Validates a value against given constraints. + */ + public function validate( + mixed $value, + null|array|Constraint $constraints = null, + null|array|GroupSequence|string $groups = null + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + return $validator->validate($value, $constraints, $groups); + } + + /** + * Validates that a value is contained in a given set of choices. + * + * @SuppressWarnings("PHPMD.ExcessiveParameterList") + */ + public function validateChoice( + mixed $value, + array|string $options = [], + ?array $choices = null, + null|callable|string $callback = null, + ?bool $multiple = null, + ?bool $strict = null, + ?int $min = null, + ?int $max = null, + ?string $message = null, + ?string $multipleMessage = null, + ?string $minMessage = null, + ?string $maxMessage = null, + ?array $groups = null, + mixed $payload = null, + ?bool $match = null + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\Choice( + options: $options, + choices: $choices, + callback: $callback, + multiple: $multiple, + strict: $strict, + min: $min, + max: $max, + message: $message, + multipleMessage: $multipleMessage, + minMessage: $minMessage, + maxMessage: $maxMessage, + groups: $groups, + payload: $payload, + match: $match, + ), + ]); + } + + /** + * Validates the count of elements in a given value. + * + * @SuppressWarnings("PHPMD.ExcessiveParameterList") + */ + public function validateCount( + mixed $value, + null|array|int $exactly = null, + ?int $min = null, + ?int $max = null, + ?int $divisibleBy = null, + ?string $exactMessage = null, + ?string $minMessage = null, + ?string $maxMessage = null, + ?string $divisibleByMessage = null, + ?array $groups = null, + mixed $payload = null, + array $options = [] + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\Count( + exactly: $exactly, + min: $min, + max: $max, + divisibleBy: $divisibleBy, + exactMessage: $exactMessage, + minMessage: $minMessage, + maxMessage: $maxMessage, + divisibleByMessage: $divisibleByMessage, + groups: $groups, + payload: $payload, + options: $options, + ), + ]); + } + + /** + * Validates that a value is a valid date. + */ + public function validateDate( + mixed $value, + ?array $options = null, + ?string $message = null, + ?array $groups = null, + mixed $payload = null + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\NotBlank( + message: $message, + ), + new Constraints\Date( + options: $options, + message: $message, + groups: $groups, + payload: $payload, + ), + ]); + } + + public function validateDateTime( + mixed $value, + null|array|string $format = null, + ?string $message = null, + ?array $groups = null, + mixed $payload = null, + array $options = [] + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\NotBlank( + message: $message, + ), + new Constraints\DateTime( + format: $format, + message: $message, + groups: $groups, + payload: $payload, + options: $options, + ), + ]); + } + + /** + * Validates that a value is a valid email address. + */ + public function validateEmail( + mixed $value, + ?array $options = null, + ?string $message = null, + ?string $mode = null, + ?callable $normalizer = null, + ?array $groups = null, + mixed $payload = null + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\NotBlank( + message: $message, + ), + new Constraints\Email( + options: $options, + message: $message, + mode: $mode, + normalizer: $normalizer, + groups: $groups, + payload: $payload, + ), + ]); + } + + /** + * Validates that a file meets given constraints. + * + * @SuppressWarnings("PHPMD.ExcessiveParameterList") + */ + public function validateFile( + mixed $value, + ?array $options = null, + null|int|string $maxSize = null, + ?bool $binaryFormat = null, + null|array|string $mimeTypes = null, + ?int $filenameMaxLength = null, + ?string $notFoundMessage = null, + ?string $notReadableMessage = null, + ?string $maxSizeMessage = null, + ?string $mimeTypesMessage = null, + ?string $disallowEmptyMessage = null, + ?string $filenameTooLongMessage = null, + ?string $uploadIniSizeErrorMessage = null, + ?string $uploadFormSizeErrorMessage = null, + ?string $uploadPartialErrorMessage = null, + ?string $uploadNoFileErrorMessage = null, + ?string $uploadNoTmpDirErrorMessage = null, + ?string $uploadCantWriteErrorMessage = null, + ?string $uploadExtensionErrorMessage = null, + ?string $uploadErrorMessage = null, + ?array $groups = null, + mixed $payload = null, + null|array|string $extensions = null, + ?string $extensionsMessage = null + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate(value: $value, constraints: [ + new Constraints\File( + options: $options, + maxSize: $maxSize, + binaryFormat: $binaryFormat, + mimeTypes: $mimeTypes, + filenameMaxLength: $filenameMaxLength, + notFoundMessage: $notFoundMessage, + notReadableMessage: $notReadableMessage, + maxSizeMessage: $maxSizeMessage, + mimeTypesMessage: $mimeTypesMessage, + disallowEmptyMessage: $disallowEmptyMessage, + filenameTooLongMessage: $filenameTooLongMessage, + uploadIniSizeErrorMessage: $uploadIniSizeErrorMessage, + uploadFormSizeErrorMessage: $uploadFormSizeErrorMessage, + uploadPartialErrorMessage: $uploadPartialErrorMessage, + uploadNoFileErrorMessage: $uploadNoFileErrorMessage, + uploadNoTmpDirErrorMessage: $uploadNoTmpDirErrorMessage, + uploadCantWriteErrorMessage: $uploadCantWriteErrorMessage, + uploadExtensionErrorMessage: $uploadExtensionErrorMessage, + uploadErrorMessage: $uploadErrorMessage, + groups: $groups, + payload: $payload, + extensions: $extensions, + extensionsMessage: $extensionsMessage, + ), + ]); + } + + /** + * Validates that a value is identical to the given one. + */ + public function validateIdentical( + mixed $value, + mixed $compare, + ?string $propertyPath = null, + ?string $message = null, + ?array $groups = null, + mixed $payload = null, + array $options = [] + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\IdenticalTo( + value: $compare, + propertyPath: $propertyPath, + message: $message, + groups: $groups, + payload: $payload, + options: $options, + ), + ]); + } + + /** + * Validates that a value is a valid image. + * + * @SuppressWarnings("PHPMD.ExcessiveParameterList") + */ + public function validateImage( + mixed $value, + ?array $options = null, + null|int|string $maxSize = null, + ?bool $binaryFormat = null, + ?array $mimeTypes = null, + ?int $filenameMaxLength = null, + ?int $minWidth = null, + ?int $maxWidth = null, + ?int $maxHeight = null, + ?int $minHeight = null, + null|float|int $maxRatio = null, + null|float|int $minRatio = null, + null|float|int $minPixels = null, + null|float|int $maxPixels = null, + ?bool $allowSquare = null, + ?bool $allowLandscape = null, + ?bool $allowPortrait = null, + ?bool $detectCorrupted = null, + ?string $notFoundMessage = null, + ?string $notReadableMessage = null, + ?string $maxSizeMessage = null, + ?string $mimeTypesMessage = null, + ?string $disallowEmptyMessage = null, + ?string $filenameTooLongMessage = null, + ?string $uploadIniSizeErrorMessage = null, + ?string $uploadFormSizeErrorMessage = null, + ?string $uploadPartialErrorMessage = null, + ?string $uploadNoFileErrorMessage = null, + ?string $uploadNoTmpDirErrorMessage = null, + ?string $uploadCantWriteErrorMessage = null, + ?string $uploadExtensionErrorMessage = null, + ?string $uploadErrorMessage = null, + ?string $sizeNotDetectedMessage = null, + ?string $maxWidthMessage = null, + ?string $minWidthMessage = null, + ?string $maxHeightMessage = null, + ?string $minHeightMessage = null, + ?string $minPixelsMessage = null, + ?string $maxPixelsMessage = null, + ?string $maxRatioMessage = null, + ?string $minRatioMessage = null, + ?string $allowSquareMessage = null, + ?string $allowLandscapeMessage = null, + ?string $allowPortraitMessage = null, + ?string $corruptedMessage = null, + ?array $groups = null, + mixed $payload = null, + null|array|string $extensions = null, + ?string $extensionsMessage = null + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\Image( + options: $options, + maxSize: $maxSize, + binaryFormat: $binaryFormat, + mimeTypes: $mimeTypes, + filenameMaxLength: $filenameMaxLength, + minWidth: $minWidth, + maxWidth: $maxWidth, + maxHeight: $maxHeight, + minHeight: $minHeight, + maxRatio: $maxRatio, + minRatio: $minRatio, + minPixels: $minPixels, + maxPixels: $maxPixels, + allowSquare: $allowSquare, + allowLandscape: $allowLandscape, + allowPortrait: $allowPortrait, + detectCorrupted: $detectCorrupted, + notFoundMessage: $notFoundMessage, + notReadableMessage: $notReadableMessage, + maxSizeMessage: $maxSizeMessage, + mimeTypesMessage: $mimeTypesMessage, + disallowEmptyMessage: $disallowEmptyMessage, + filenameTooLongMessage: $filenameTooLongMessage, + uploadIniSizeErrorMessage: $uploadIniSizeErrorMessage, + uploadFormSizeErrorMessage: $uploadFormSizeErrorMessage, + uploadPartialErrorMessage: $uploadPartialErrorMessage, + uploadNoFileErrorMessage: $uploadNoFileErrorMessage, + uploadNoTmpDirErrorMessage: $uploadNoTmpDirErrorMessage, + uploadCantWriteErrorMessage: $uploadCantWriteErrorMessage, + uploadExtensionErrorMessage: $uploadExtensionErrorMessage, + uploadErrorMessage: $uploadErrorMessage, + sizeNotDetectedMessage: $sizeNotDetectedMessage, + maxWidthMessage: $maxWidthMessage, + minWidthMessage: $minWidthMessage, + maxHeightMessage: $maxHeightMessage, + minHeightMessage: $minHeightMessage, + minPixelsMessage: $minPixelsMessage, + maxPixelsMessage: $maxPixelsMessage, + maxRatioMessage: $maxRatioMessage, + minRatioMessage: $minRatioMessage, + allowSquareMessage: $allowSquareMessage, + allowLandscapeMessage: $allowLandscapeMessage, + allowPortraitMessage: $allowPortraitMessage, + corruptedMessage: $corruptedMessage, + groups: $groups, + payload: $payload, + extensions: $extensions, + extensionsMessage: $extensionsMessage, + ), + ]); + } + + /** + * Validates the length of a given value. + * + * @SuppressWarnings("PHPMD.ExcessiveParameterList") + */ + public function validateLength( + mixed $value, + null|array|int $exactly = null, + ?int $min = null, + ?int $max = null, + ?string $charset = null, + ?callable $normalizer = null, + ?string $countUnit = null, + ?string $exactMessage = null, + ?string $minMessage = null, + ?string $maxMessage = null, + ?string $charsetMessage = null, + ?array $groups = null, + mixed $payload = null, + array $options = [] + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\Length( + exactly: $exactly, + min: $min, + max: $max, + charset: $charset, + normalizer: $normalizer, + countUnit: $countUnit, + exactMessage: $exactMessage, + minMessage: $minMessage, + maxMessage: $maxMessage, + charsetMessage: $charsetMessage, + groups: $groups, + payload: $payload, + options: $options, + ), + ]); + } + + /** + * Validates that a value is not empty. + */ + public function validateNotEmpty( + mixed $value, + ?array $options = null, + ?string $message = null, + ?bool $allowNull = null, + ?callable $normalizer = null, + ?array $groups = null, + mixed $payload = null + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\NotBlank( + options: $options, + message: $message, + allowNull: $allowNull, + normalizer: $normalizer, + groups: $groups, + payload: $payload, + ), + ]); + } + + /** + * Validates that a password meets given constraints. + */ + public function validatePassword( + mixed $value, + ?int $min = null, + ?int $max = null, + ?string $emptyMessage = null, + ?string $minMessage = null, + ?string $maxMessage = null, + ?string $regexMessage = null, + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\NotBlank( + message: $emptyMessage, + ), + new Constraints\Length( + min: $min, + max: $max, + minMessage: $minMessage, + maxMessage: $maxMessage, + ), + new Constraints\Regex( + pattern: '/^(?=.*[a-z])(?=.*[0-9]).+$/iu', + message: $regexMessage, + ), + ]); + } + + /** + * Validates that a value is within a given range. + * + * @SuppressWarnings("PHPMD.ExcessiveParameterList") + */ + public function validateRange( + mixed $value, + ?array $options = null, + ?string $notInRangeMessage = null, + ?string $minMessage = null, + ?string $maxMessage = null, + ?string $invalidMessage = null, + ?string $invalidDateTimeMessage = null, + mixed $min = null, + ?string $minPropertyPath = null, + mixed $max = null, + ?string $maxPropertyPath = null, + ?array $groups = null, + mixed $payload = null + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\Range( + options: $options, + notInRangeMessage: $notInRangeMessage, + minMessage: $minMessage, + maxMessage: $maxMessage, + invalidMessage: $invalidMessage, + invalidDateTimeMessage: $invalidDateTimeMessage, + min: $min, + minPropertyPath: $minPropertyPath, + max: $max, + maxPropertyPath: $maxPropertyPath, + groups: $groups, + payload: $payload, + ), + ]); + } + + /** + * Validates a value against a regular expression. + */ + public function validateRegex( + mixed $value, + ?string $pattern, + ?string $message = null, + ?string $htmlPattern = null, + ?bool $match = null, + ?callable $normalizer = null, + ?array $groups = null, + mixed $payload = null, + array $options = [] + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\Regex( + pattern: $pattern, + message: $message, + htmlPattern: $htmlPattern, + match: $match, + normalizer: $normalizer, + groups: $groups, + payload: $payload, + options: $options, + ), + ]); + } + + /** + * Validates that a value is of a given type. + */ + public function validateType( + mixed $value, + ?string $type, + ?string $message = null, + ?array $groups = null, + mixed $payload = null, + array $options = [] + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\Type( + type: $type, + message: $message, + groups: $groups, + payload: $payload, + options: $options, + ), + ]); + } + + /** + * Validates that a value is a valid URL. + */ + public function validateUrl( + mixed $value, + ?array $options = null, + ?string $message = null, + ?array $protocols = null, + ?bool $relativeProtocol = null, + ?callable $normalizer = null, + ?array $groups = null, + mixed $payload = null + ): ConstraintViolationListInterface { + $validator = self::getValidator(); + + return $validator->validate($value, [ + new Constraints\NotBlank( + message: $message, + ), + new Constraints\Url( + options: $options, + message: $message, + protocols: $protocols, + relativeProtocol: $relativeProtocol, + normalizer: $normalizer, + groups: $groups, + payload: $payload, + ), + ]); + } + + /** + * @throws Mage_Core_Exception + * @return Constraint[] + */ + public function getContraintsByType(string $type, array $options = []): array + { + $message = $options['message'] ?? null; + + return match ($type) { + 'Alnum' => [new Constraints\Type(type: 'alnum', message: $message)], + 'Alpha' => [new Constraints\Type(type: 'alpha', message: $message)], + 'Between' => [new Constraints\Range(min: $options['min'] ?? null, max: $options['max'] ?? null)], + 'Callback' => [new Constraints\Callback($options)], + 'Ccnum' => [new Constraints\Luhn($options)], + 'CreditCard' => [new Constraints\CardScheme($options)], + 'Date' => [new Constraints\Date($options)], + 'Digits' => [new Constraints\Type(type: 'digit', message: $message)], + 'Float' => [new Constraints\Type(type: 'float', message: $message)], + 'GreaterThan' => [new Constraints\GreaterThan($options)], + 'Hostname' => [new Constraints\Hostname($options)], + 'Iban' => [new Constraints\Iban($options)], + 'Identical' => [new Constraints\IdenticalTo($options)], + 'InArray' => [new Constraints\Choice($options)], + 'Int' => [new Constraints\Type(type: 'int', message: $message)], + 'Ip' => [new Constraints\Ip($options)], + 'Isbn' => [new Constraints\Isbn($options)], + 'LessThan' => [new Constraints\LessThan($options)], + 'NoEmpty' => [new Constraints\NotBlank($options)], + 'Regex' => [new Constraints\Regex(pattern: $options['pattern'] ?? '')], + 'StringLength' => [new Constraints\Length($options)], + default => throw new Mage_Core_Exception("Validator $type does not exist") + }; + } + + public function getErrorMessages(array|ArrayObject $violations): ?ArrayObject + { + $errors = new ArrayObject(); + foreach ($violations as $violation) { + if ($violation instanceof ConstraintViolationListInterface) { + foreach ($violation as $error) { + $errors->append($error->getMessage()); + } + } + } + + if (count($errors) === 0) { + return null; + } + + return $errors; + } +} diff --git a/app/code/core/Mage/Core/Helper/Validate/Abstract.php b/app/code/core/Mage/Core/Helper/Validate/Abstract.php new file mode 100644 index 00000000000..91e0e4a6bd4 --- /dev/null +++ b/app/code/core/Mage/Core/Helper/Validate/Abstract.php @@ -0,0 +1,222 @@ +_messages; + } + + /** + * Returns an array of the names of variables that are used in constructing validation failure messages + */ + public function getMessageVariables(): array + { + return array_keys($this->_messageVariables); + } + + /** + * Returns the message templates from the validator + */ + public function getMessageTemplates(): array + { + return $this->_messageTemplates; + } + + /** + * Sets the validation failure message template for a particular key + * + * @throws Mage_Core_Exception + * @return $this + */ + public function setMessage(string $messageString, ?string $messageKey = null) + { + if ($messageKey === null) { + $keys = array_keys($this->_messageTemplates); + foreach ($keys as $key) { + $this->setMessage($messageString, $key); + } + + return $this; + } + + if (!isset($this->_messageTemplates[$messageKey])) { + throw new Mage_Core_Exception("No message template exists for key '$messageKey'"); + } + + $this->_messageTemplates[$messageKey] = $messageString; + return $this; + } + + /** + * Sets validation failure message templates given as an array, where the array keys are the message keys, + * and the array values are the message template strings. + * + * @throws Mage_Core_Exception + * @return $this + */ + public function setMessages(array $messages) + { + foreach ($messages as $key => $message) { + $this->setMessage($message, $key); + } + + return $this; + } + + /** + * Magic function returns the value of the requested property, if and only if it is the value or a + * message variable. + * + * @throws Mage_Core_Exception + * @return mixed + */ + public function __get(string $property) + { + if ($property == 'value') { + return $this->_value; + } + + if (array_key_exists($property, $this->_messageVariables)) { + return $this->{$this->_messageVariables[$property]}; + } + + throw new Mage_Core_Exception("No property exists by the name '$property'"); + } + + /** + * Constructs and returns a validation failure message with the given message key and value. + * + * Returns null if and only if $messageKey does not correspond to an existing template. + */ + final protected function _createMessage(string $messageKey, array|object|string $value): ?string + { + if (!isset($this->_messageTemplates[$messageKey])) { + return null; + } + + $message = $this->_messageTemplates[$messageKey]; + + if (is_object($value)) { + if (!in_array('__toString', get_class_methods($value))) { + $value = $value::class . ' object'; + } else { + $value = $value->__toString(); + } + } elseif (is_array($value)) { + $value = $this->_implodeRecursive($value); + } else { + $value = implode('', (array) $value); + } + + $message = str_replace('%value%', $value, $message); + foreach ($this->_messageVariables as $ident => $property) { + $message = str_replace( + "%$ident%", + implode(' ', (array) $this->$property), + $message, + ); + } + + return $message; + } + + public function createMessageFromTemplate(string $template): string + { + $message = str_replace('%value%', $this->_value, $template); + foreach ($this->_messageVariables as $ident => $property) { + $message = str_replace( + "%$ident%", + implode(' ', (array) $this->$property), + $message, + ); + } + + return $message; + } + + /** + * Joins elements of a multidimensional array + */ + final protected function _implodeRecursive(array $pieces): string + { + $values = []; + foreach ($pieces as $item) { + if (is_array($item)) { + $values[] = $this->_implodeRecursive($item); + } else { + $values[] = $item; + } + } + + return implode(', ', $values); + } + + final protected function _error(?string $messageKey, ?string $value = null): void + { + if ($messageKey === null) { + $keys = array_keys($this->_messageTemplates); + $messageKey = current($keys); + } + + if ($value === null) { + $value = $this->_value; + } + + $this->_messages[$messageKey] = $this->_createMessage($messageKey, $value); + } + + /** + * Sets the value to be validated and clears the messages and errors arrays + */ + final protected function _setValue(mixed $value): void + { + $this->_value = $value; + $this->_messages = []; + } +} diff --git a/app/code/core/Mage/Core/Helper/Validate/Interface.php b/app/code/core/Mage/Core/Helper/Validate/Interface.php new file mode 100644 index 00000000000..b9d16e3d421 --- /dev/null +++ b/app/code/core/Mage/Core/Helper/Validate/Interface.php @@ -0,0 +1,41 @@ +isModuleEnabled($moduleName); } + + protected function getValidationHelper(): Mage_Core_Helper_Validate + { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + return $validator; + } } diff --git a/app/code/core/Mage/Core/Model/App.php b/app/code/core/Mage/Core/Model/App.php index 64da31cc987..90314ed0819 100644 --- a/app/code/core/Mage/Core/Model/App.php +++ b/app/code/core/Mage/Core/Model/App.php @@ -992,7 +992,7 @@ public function getDistroLocaleCode() /** * Retrieve application website object * - * @param null|int|Mage_Core_Model_Website|string|true $id + * @param null|bool|int|Mage_Core_Model_Website|string $id * @return Mage_Core_Model_Website */ public function getWebsite($id = null) diff --git a/app/code/core/Mage/Core/Model/Email/Queue.php b/app/code/core/Mage/Core/Model/Email/Queue.php index adbabd508ce..426fea8a66a 100644 --- a/app/code/core/Mage/Core/Model/Email/Queue.php +++ b/app/code/core/Mage/Core/Model/Email/Queue.php @@ -30,8 +30,8 @@ * @method string getMessageBody() * @method $this setMessageBody(string $value) * @method $this setMessageBodyHash(string $value) - * @method string getMessageParameters() - * @method $this setMessageParameters(string $value) + * @method array getMessageParameters() + * @method $this setMessageParameters(array $value) * @method $this setProcessedAt(string $value) */ class Mage_Core_Model_Email_Queue extends Mage_Core_Model_Abstract diff --git a/app/code/core/Mage/Core/Model/Email/Template/Abstract.php b/app/code/core/Mage/Core/Model/Email/Template/Abstract.php index 39c728a6fc5..c928b7d8e27 100644 --- a/app/code/core/Mage/Core/Model/Email/Template/Abstract.php +++ b/app/code/core/Mage/Core/Model/Email/Template/Abstract.php @@ -231,9 +231,8 @@ protected function _getCssFileContent($filename) '_theme' => $theme, ], ); - $validator = new Zend_Validate_File_Extension('css'); - if ($validator->isValid($filePath) && is_readable($filePath)) { + if ($this->validateFileExension($filePath, 'css') && is_readable($filePath)) { return (string) file_get_contents($filePath); } @@ -241,6 +240,19 @@ protected function _getCssFileContent($filename) return ''; } + public function validateFileExension(string $filePath, string $extension): bool + { + if ($extension === 'css') { + $extension = ['css' => ['text/css', 'text/plain']]; + } + + $validator = $this->getValidationHelper(); + return $validator->validateFile( + value: $filePath, + extensions: $extension, + )->count() === 0; + } + /** * Accepts a path to a System Config setting that contains a comma-delimited list of files to load. Loads those * files and then returns the concatenated content. diff --git a/app/code/core/Mage/Core/Model/File/Validator/AvailablePath.php b/app/code/core/Mage/Core/Model/File/Validator/AvailablePath.php index 05c0000a01d..2ffd8359342 100644 --- a/app/code/core/Mage/Core/Model/File/Validator/AvailablePath.php +++ b/app/code/core/Mage/Core/Model/File/Validator/AvailablePath.php @@ -27,7 +27,7 @@ * * @package Mage_Core */ -class Mage_Core_Model_File_Validator_AvailablePath extends Zend_Validate_Abstract +class Mage_Core_Model_File_Validator_AvailablePath extends Mage_Core_Helper_Validate_Abstract { public const PROTECTED_PATH = 'protectedPath'; diff --git a/app/code/core/Mage/Core/Model/File/Validator/NotProtectedExtension.php b/app/code/core/Mage/Core/Model/File/Validator/NotProtectedExtension.php index 2e694696d6a..65f3cd47908 100644 --- a/app/code/core/Mage/Core/Model/File/Validator/NotProtectedExtension.php +++ b/app/code/core/Mage/Core/Model/File/Validator/NotProtectedExtension.php @@ -12,7 +12,7 @@ * * @package Mage_Core */ -class Mage_Core_Model_File_Validator_NotProtectedExtension extends Zend_Validate_Abstract +class Mage_Core_Model_File_Validator_NotProtectedExtension extends Mage_Core_Helper_Validate_Abstract { public const PROTECTED_EXTENSION = 'protectedExtension'; diff --git a/app/code/core/Mage/Core/Model/Layout/Validator.php b/app/code/core/Mage/Core/Model/Layout/Validator.php index 7c806a7c648..7ced462bd2d 100644 --- a/app/code/core/Mage/Core/Model/Layout/Validator.php +++ b/app/code/core/Mage/Core/Model/Layout/Validator.php @@ -14,7 +14,7 @@ * * @package Mage_Core */ -class Mage_Core_Model_Layout_Validator extends Zend_Validate_Abstract +class Mage_Core_Model_Layout_Validator extends Mage_Core_Helper_Validate_Abstract { public const XML_PATH_LAYOUT_DISALLOWED_BLOCKS = 'validators/custom_layout/disallowed_block'; diff --git a/app/code/core/Mage/Core/Model/Resource/Design.php b/app/code/core/Mage/Core/Model/Resource/Design.php index dbcd40a2ea5..c109f4563ec 100644 --- a/app/code/core/Mage/Core/Model/Resource/Design.php +++ b/app/code/core/Mage/Core/Model/Resource/Design.php @@ -22,14 +22,18 @@ protected function _construct() /** * @param Mage_Core_Model_Design $object * @inheritDoc + * @throws Mage_Core_Exception */ public function _beforeSave(Mage_Core_Model_Abstract $object) { $dateFrom = $object->getDateFrom(); $dateTo = $object->getDateTo(); if (!empty($dateFrom) && !empty($dateTo)) { - $validator = new Zend_Validate_Date(); - if (!$validator->isValid($dateFrom) || !$validator->isValid($dateTo)) { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if ($validator->validateDateTime(value: $dateFrom)->count() > 0 + || $validator->validateDateTime(value: $dateTo)->count() > 0 + ) { Mage::throwException(Mage::helper('core')->__('Invalid date')); } diff --git a/app/code/core/Mage/Core/Model/Url/Validator.php b/app/code/core/Mage/Core/Model/Url/Validator.php index f64649db6ee..50c7eae403c 100644 --- a/app/code/core/Mage/Core/Model/Url/Validator.php +++ b/app/code/core/Mage/Core/Model/Url/Validator.php @@ -12,22 +12,13 @@ * * @package Mage_Core */ -class Mage_Core_Model_Url_Validator extends Zend_Validate_Abstract +class Mage_Core_Model_Url_Validator extends Mage_Core_Helper_Validate_Abstract { /** * Error keys */ public const INVALID_URL = 'invalidUrl'; - /** - * Object constructor - */ - public function __construct() - { - // set translated message template - $this->setMessage(Mage::helper('core')->__("Invalid URL '%value%'."), self::INVALID_URL); - } - /** * Validation failure message template definitions * @@ -37,6 +28,19 @@ public function __construct() self::INVALID_URL => "Invalid URL '%value%'.", ]; + /** + * Object constructor + * @throws Exception + */ + public function __construct() + { + // set translated message template + $this->setMessage( + Mage::helper('core')->__($this->_messageTemplates[self::INVALID_URL]), + self::INVALID_URL, + ); + } + /** * Validate value * @@ -47,8 +51,11 @@ public function isValid($value) { $this->_setValue($value); + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + //check valid URL - if (!Zend_Uri::check($value)) { + if ($validator->validateUrl(value: $value)->count() > 0) { $this->_error(self::INVALID_URL); return false; } diff --git a/app/code/core/Mage/Core/Model/Variable.php b/app/code/core/Mage/Core/Model/Variable.php index 76747799e9b..6e0e42a7f29 100644 --- a/app/code/core/Mage/Core/Model/Variable.php +++ b/app/code/core/Mage/Core/Model/Variable.php @@ -104,7 +104,7 @@ public function getValue($type = null) /** * Validation of object data. Checking for unique variable code * - * @return bool|string + * @return string|true */ public function validate() { diff --git a/app/code/core/Mage/Customer/Model/Address/Abstract.php b/app/code/core/Mage/Customer/Model/Address/Abstract.php index 511ee05fd24..1cc48ce977a 100644 --- a/app/code/core/Mage/Customer/Model/Address/Abstract.php +++ b/app/code/core/Mage/Customer/Model/Address/Abstract.php @@ -450,39 +450,53 @@ public function validate() */ protected function _basicCheck() { - if (!Zend_Validate::is($this->getFirstname(), 'NotEmpty')) { - $this->addError(Mage::helper('customer')->__('Please enter the first name.')); - } - - if (!Zend_Validate::is($this->getLastname(), 'NotEmpty')) { - $this->addError(Mage::helper('customer')->__('Please enter the last name.')); - } - - if (!Zend_Validate::is($this->getStreet(1), 'NotEmpty')) { - $this->addError(Mage::helper('customer')->__('Please enter the street.')); - } - - if (!Zend_Validate::is($this->getCity(), 'NotEmpty')) { - $this->addError(Mage::helper('customer')->__('Please enter the city.')); - } - - if (!Zend_Validate::is($this->getTelephone(), 'NotEmpty')) { - $this->addError(Mage::helper('customer')->__('Please enter the telephone number.')); + $validator = $this->getValidationHelper(); + $violations = new ArrayObject(); + + $violations->append($validator->validateNotEmpty( + value: $this->getFirstname(), + message: Mage::helper('customer')->__('Please enter the first name.'), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getLastname(), + message: Mage::helper('customer')->__('Please enter the last name.'), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getStreet(1), + message: Mage::helper('customer')->__('Please enter the street.'), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getCity(), + message: Mage::helper('customer')->__('Please enter the city.'), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getTelephone(), + message: Mage::helper('customer')->__('Please enter the telephone number.'), + )); + + foreach ($violations as $violation) { + foreach ($violation as $error) { + $this->addError($error->getMessage()); + } } $havingOptionalZip = Mage::helper('directory')->getCountriesWithOptionalZip(); if (!in_array($this->getCountryId(), $havingOptionalZip) - && !Zend_Validate::is($this->getPostcode(), 'NotEmpty') + && $validator->validateNotEmpty($this->getPostcode())->count() > 0 ) { $this->addError(Mage::helper('customer')->__('Please enter the zip/postal code.')); } - if (!Zend_Validate::is($this->getCountryId(), 'NotEmpty')) { + if ($validator->validateNotEmpty($this->getCountryId())->count() > 0) { $this->addError(Mage::helper('customer')->__('Please enter the country.')); } if ($this->getCountryModel()->getRegionCollection()->getSize() - && !Zend_Validate::is($this->getRegionId(), 'NotEmpty') + && $validator->validateNotEmpty($this->getRegionId())->count() > 0 && Mage::helper('directory')->isRegionRequired($this->getCountryId()) ) { $this->addError(Mage::helper('customer')->__('Please enter the state/province.')); diff --git a/app/code/core/Mage/Customer/Model/Customer.php b/app/code/core/Mage/Customer/Model/Customer.php index 2323e7d5495..ee859953e49 100644 --- a/app/code/core/Mage/Customer/Model/Customer.php +++ b/app/code/core/Mage/Customer/Model/Customer.php @@ -7,6 +7,8 @@ * @package Mage_Customer */ +use Symfony\Component\Validator\ConstraintViolationListInterface; + /** * Customer model * @@ -1061,102 +1063,107 @@ public function setStore(Mage_Core_Model_Store $store) * only when password is set (i.e. its change is requested) * * @throws Mage_Core_Exception - * @throws Zend_Validate_Exception * @return array|true */ public function validate() { - $errors = []; - if (!Zend_Validate::is(trim($this->getFirstname()), 'NotEmpty')) { - $errors[] = Mage::helper('customer')->__('The first name cannot be empty.'); - } - - if (!Zend_Validate::is(trim($this->getLastname()), 'NotEmpty')) { - $errors[] = Mage::helper('customer')->__('The last name cannot be empty.'); - } + $validator = $this->getValidationHelper(); + $violations = new ArrayObject(); - if (!Zend_Validate::is($this->getEmail(), 'EmailAddress')) { - $errors[] = Mage::helper('customer')->__('Invalid email address "%s".', $this->getEmail()); - } + $violations->append($validator->validateNotEmpty( + value: $this->getFirstname(), + message: Mage::helper('customer')->__('The first name cannot be empty.'), + )); - $password = $this->getPassword(); - if (!$this->getId() && !Zend_Validate::is($password, 'NotEmpty')) { - $errors[] = Mage::helper('customer')->__('The password cannot be empty.'); - } + $violations->append($validator->validateNotEmpty( + value: $this->getLastname(), + message: Mage::helper('customer')->__('The last name cannot be empty.'), + )); - $minPasswordLength = $this->getMinPasswordLength(); - if (strlen($password) && !Zend_Validate::is($password, 'StringLength', [$minPasswordLength])) { - $errors[] = Mage::helper('customer') - ->__('The minimum password length is %s', $minPasswordLength); - } + $email = $this->getEmail(); + $violations->append($validator->validateEmail( + value: $email, + message: Mage::helper('customer')->__('Invalid email address "%s".', $email), + )); - if (strlen($password) && !Zend_Validate::is($password, 'StringLength', ['max' => self::MAXIMUM_PASSWORD_LENGTH])) { - $errors[] = Mage::helper('customer') - ->__('Please enter a password with at most %s characters.', self::MAXIMUM_PASSWORD_LENGTH); - } + $violations->append($this->getPasswordValidator(value: $this->getPassword())); - $confirmation = $this->getPasswordConfirmation(); - if ($password != $confirmation) { - $errors[] = Mage::helper('customer')->__('Please make sure your passwords match.'); - } + $violations->append($validator->validateIdentical( + value: $this->getPasswordConfirmation(), + compare: $this->getPassword(), + message: Mage::helper('customer')->__('Please make sure your passwords match.'), + )); $entityType = Mage::getSingleton('eav/config')->getEntityType('customer'); - $attribute = Mage::getModel('customer/attribute')->loadByCode($entityType, 'dob'); - if ($attribute->getIsRequired() && trim($this->getDob()) == '') { - $errors[] = Mage::helper('customer')->__('The Date of Birth is required.'); + + if ($this->shouldValidateDob($entityType)) { + $violations->append($validator->validateDate( + value: trim($this->getDob()), + message: Mage::helper('customer')->__('The Date of Birth is required.'), + )); } - $attribute = Mage::getModel('customer/attribute')->loadByCode($entityType, 'taxvat'); - if ($attribute->getIsRequired() && trim($this->getTaxvat()) == '') { - $errors[] = Mage::helper('customer')->__('The TAX/VAT number is required.'); + if ($this->shouldValidateTaxvat($entityType)) { + $violations->append($validator->validateNotEmpty( + value: trim($this->getTaxvat()), + message: Mage::helper('customer')->__('The TAX/VAT number is required.'), + )); } - $attribute = Mage::getModel('customer/attribute')->loadByCode($entityType, 'gender'); - if ($attribute->getIsRequired() && trim($this->getGender()) == '') { - $errors[] = Mage::helper('customer')->__('Gender is required.'); + if ($this->shouldValidateGender($entityType)) { + $violations->append($validator->validateNotEmpty( + value: trim($this->getGender()), + message: Mage::helper('customer')->__('Gender is required.'), + )); } - if (empty($errors)) { + $errors = $validator->getErrorMessages($violations); + if (!$errors) { return true; } - return $errors; + return (array) $errors; } /** * Validate customer password on reset - * @throws Zend_Validate_Exception * @return array|true */ public function validateResetPassword() { - $errors = []; - $password = $this->getPassword(); - if (!Zend_Validate::is($password, 'NotEmpty')) { - $errors[] = Mage::helper('customer')->__('The password cannot be empty.'); - } + $validator = $this->getValidationHelper(); + $violations = new ArrayObject(); - $minPasswordLength = $this->getMinPasswordLength(); - if (!Zend_Validate::is($password, 'StringLength', [$minPasswordLength])) { - $errors[] = Mage::helper('customer') - ->__('The minimum password length is %s', $minPasswordLength); - } + $violations->append($this->getPasswordValidator(value: $this->getPassword())); - if (!Zend_Validate::is($password, 'StringLength', ['max' => self::MAXIMUM_PASSWORD_LENGTH])) { - $errors[] = Mage::helper('customer') - ->__('Please enter a password with at most %s characters.', self::MAXIMUM_PASSWORD_LENGTH); - } + $violations->append($validator->validateIdentical( + value: $this->getPasswordConfirmation(), + compare: $this->getPassword(), + message: Mage::helper('customer')->__('Please make sure your passwords match.'), + )); - $confirmation = $this->getPasswordConfirmation(); - if ($password != $confirmation) { - $errors[] = Mage::helper('customer')->__('Please make sure your passwords match.'); - } - - if (empty($errors)) { + $errors = $validator->getErrorMessages($violations); + if (!$errors) { return true; } - return $errors; + return (array) $errors; + } + + public function getPasswordValidator(mixed $value): ConstraintViolationListInterface + { + $min = $this->getMinPasswordLength(); + $validator = $this->getValidationHelper(); + + return $validator->validatePassword( + value: $value, + min: $min, + max: self::MAXIMUM_PASSWORD_LENGTH, + emptyMessage: Mage::helper('customer')->__('The password cannot be empty.'), + minMessage: Mage::helper('customer')->__('The minimum password length is %s', $min), + maxMessage: Mage::helper('customer')->__('Please enter a password with at most %s characters.', self::MAXIMUM_PASSWORD_LENGTH), + regexMessage: Mage::helper('customer')->__('Password must include both numeric and alphabetic characters.'), + ); } /** @@ -1721,4 +1728,38 @@ public function getMinPasswordLength() $absoluteMinLength = Mage_Core_Model_App::ABSOLUTE_MIN_PASSWORD_LENGTH; return max($absoluteMinLength, $minLength); } + + /** + * @throws Mage_Core_Exception + */ + public function shouldValidateDob($entityType): bool + { + $attribute = $this->getCustomerAttributeModel()->loadByCode($entityType, 'dob'); + return $attribute->getIsRequired(); + } + + /** + * @throws Mage_Core_Exception + */ + public function shouldValidateGender($entityType): bool + { + $attribute = $this->getCustomerAttributeModel()->loadByCode($entityType, 'gender'); + return $attribute->getIsRequired(); + } + + /** + * @throws Mage_Core_Exception + */ + public function shouldValidateTaxvat($entityType): bool + { + $attribute = $this->getCustomerAttributeModel()->loadByCode($entityType, 'taxvat'); + return $attribute->getIsRequired(); + } + + public function getCustomerAttributeModel(): Mage_Customer_Model_Attribute + { + /** @var Mage_Customer_Model_Attribute $model */ + $model = Mage::getModel('customer/attribute'); + return $model; + } } diff --git a/app/code/core/Mage/Customer/controllers/AccountController.php b/app/code/core/Mage/Customer/controllers/AccountController.php index 30ec58523a2..6e59e0d880a 100644 --- a/app/code/core/Mage/Customer/controllers/AccountController.php +++ b/app/code/core/Mage/Customer/controllers/AccountController.php @@ -731,12 +731,15 @@ public function forgotPasswordAction() /** * Forgot customer password action * @throws Mage_Core_Model_Store_Exception + * @throws Mage_Core_Exception */ public function forgotPasswordPostAction() { $email = (string) $this->getRequest()->getPost('email'); if ($email) { - if (!Zend_Validate::is($email, 'EmailAddress')) { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if ($validator->validateEmail($email)->count() > 0) { $this->_getSession()->setForgottenEmail($email); $this->_getSession()->addError($this->__('Invalid email address.')); $this->_redirect('*/*/forgotpassword'); @@ -788,11 +791,9 @@ public function forgotPasswordPostAction() $this->_getHelper('customer')->escapeHtml($email), )); $this->_redirect('*/*/'); - return; } else { $this->_getSession()->addError($this->__('Please enter your email.')); $this->_redirect('*/*/forgotpassword'); - return; } } diff --git a/app/code/core/Mage/Dataflow/Model/Convert/Adapter/Http/Curl.php b/app/code/core/Mage/Dataflow/Model/Convert/Adapter/Http/Curl.php index d5f33aa3212..15f84dabdab 100644 --- a/app/code/core/Mage/Dataflow/Model/Convert/Adapter/Http/Curl.php +++ b/app/code/core/Mage/Dataflow/Model/Convert/Adapter/Http/Curl.php @@ -20,7 +20,9 @@ public function load() $uri = $this->getVar('uri'); // validate input parameter - if (!Zend_Uri::check($uri)) { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if ($validator->validateUrl(value: $uri)->count() > 0) { $this->addException("Expecting a valid 'uri' parameter"); } diff --git a/app/code/core/Mage/Eav/Model/Adminhtml/System/Config/Source/Inputtype/Validator.php b/app/code/core/Mage/Eav/Model/Adminhtml/System/Config/Source/Inputtype/Validator.php index b23619e9852..06821f49c1d 100644 --- a/app/code/core/Mage/Eav/Model/Adminhtml/System/Config/Source/Inputtype/Validator.php +++ b/app/code/core/Mage/Eav/Model/Adminhtml/System/Config/Source/Inputtype/Validator.php @@ -12,29 +12,28 @@ * * @package Mage_Eav */ -class Mage_Eav_Model_Adminhtml_System_Config_Source_Inputtype_Validator extends Zend_Validate_InArray +class Mage_Eav_Model_Adminhtml_System_Config_Source_Inputtype_Validator extends Mage_Core_Helper_Validate_Abstract { + public const NOT_IN_ARRAY = 'notInArray'; + /** * @inheritDoc */ protected $_messageTemplates; + protected array $haystack = []; + + public function __construct() { //set data haystack /** @var Mage_Eav_Helper_Data $helper */ $helper = Mage::helper('eav'); - $haystack = $helper->getInputTypesValidatorData(); + $this->haystack = $helper->getInputTypesValidatorData(); //reset message template and set custom $this->_messageTemplates = []; $this->_initMessageTemplates(); - - //parent construct with options - parent::__construct([ - 'haystack' => $haystack, - 'strict' => true, - ]); } /** @@ -54,6 +53,18 @@ protected function _initMessageTemplates() return $this; } + public function isValid($value) + { + $this->_setValue($value); + + if (!in_array((string) $value, $this->haystack, true)) { + $this->_error(self::NOT_IN_ARRAY); + return false; + } + + return true; + } + /** * Add input type to haystack * @@ -62,8 +73,8 @@ protected function _initMessageTemplates() */ public function addInputType($type) { - if (!in_array((string) $type, $this->_haystack, true)) { - $this->_haystack[] = (string) $type; + if (!in_array((string) $type, $this->haystack, true)) { + $this->haystack[] = (string) $type; } return $this; diff --git a/app/code/core/Mage/Eav/Model/Attribute/Data/Abstract.php b/app/code/core/Mage/Eav/Model/Attribute/Data/Abstract.php index b0792b74495..ec5cd50b8d5 100644 --- a/app/code/core/Mage/Eav/Model/Attribute/Data/Abstract.php +++ b/app/code/core/Mage/Eav/Model/Attribute/Data/Abstract.php @@ -7,6 +7,8 @@ * @package Mage_Eav */ +use Symfony\Component\Validator\Constraints; + /** * EAV Attribute Abstract Data Model * @@ -266,180 +268,53 @@ protected function _validateInputRule($value) $validateRules = $this->getAttribute()->getValidateRules(); if (!empty($validateRules['input_validation'])) { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + switch ($validateRules['input_validation']) { case 'alphanumeric': - $validator = new Zend_Validate_Alnum(true); - $validator->setMessage( - Mage::helper('eav')->__('"%s" invalid type entered.', $label), - Zend_Validate_Alnum::INVALID, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" has not only alphabetic and digit characters.', $label), - Zend_Validate_Alnum::NOT_ALNUM, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is an empty string.', $label), - Zend_Validate_Alnum::STRING_EMPTY, - ); - if (!$validator->isValid($value)) { - return $validator->getMessages(); + $violations = $validator->validateType(value: $value, type: 'alnum'); + if ($violations->count() > 0) { + return [Mage::helper('eav')->__('"%s" has not only alphabetic and digit characters.', $label)]; } break; case 'numeric': - $validator = new Zend_Validate_Digits(); - $validator->setMessage( - Mage::helper('eav')->__('"%s" invalid type entered.', $label), - Zend_Validate_Digits::INVALID, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" contains not only digit characters.', $label), - Zend_Validate_Digits::NOT_DIGITS, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is an empty string.', $label), - Zend_Validate_Digits::STRING_EMPTY, - ); - if (!$validator->isValid($value)) { - return $validator->getMessages(); + $violations = $validator->validateType(value: $value, type: 'digit'); + if ($violations->count() > 0) { + return [Mage::helper('eav')->__('"%s" contains not only digit characters.', $label)]; } break; case 'alpha': - $validator = new Zend_Validate_Alpha(true); - $validator->setMessage( - Mage::helper('eav')->__('"%s" invalid type entered.', $label), - Zend_Validate_Alpha::INVALID, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" has not only alphabetic characters.', $label), - Zend_Validate_Alpha::NOT_ALPHA, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is an empty string.', $label), - Zend_Validate_Alpha::STRING_EMPTY, - ); - if (!$validator->isValid($value)) { - return $validator->getMessages(); + $violations = $validator->validateType(value: $value, type: 'alpha'); + if ($violations->count() > 0) { + return [Mage::helper('eav')->__('"%s" has not only alphabetic characters.', $label)]; } break; case 'email': - /** - $this->__("'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded") - $this->__("Invalid type given. String expected") - $this->__("'%value%' appears to be a DNS hostname but contains a dash in an invalid position") - $this->__("'%value%' does not match the expected structure for a DNS hostname") - $this->__("'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'") - $this->__("'%value%' does not appear to be a valid local network name") - $this->__("'%value%' does not appear to be a valid URI hostname") - $this->__("'%value%' appears to be an IP address, but IP addresses are not allowed") - $this->__("'%value%' appears to be a local network name but local network names are not allowed") - $this->__("'%value%' appears to be a DNS hostname but cannot extract TLD part") - $this->__("'%value%' appears to be a DNS hostname but cannot match TLD against known list") - */ - $validator = new Zend_Validate_EmailAddress(); - $validator->setMessage( - Mage::helper('eav')->__('"%s" invalid type entered.', $label), - Zend_Validate_EmailAddress::INVALID, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is not a valid email address.', $label), - Zend_Validate_EmailAddress::INVALID_FORMAT, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is not a valid hostname.', $label), - Zend_Validate_EmailAddress::INVALID_HOSTNAME, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is not a valid hostname.', $label), - Zend_Validate_EmailAddress::INVALID_MX_RECORD, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is not a valid hostname.', $label), - Zend_Validate_EmailAddress::INVALID_MX_RECORD, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is not a valid email address.', $label), - Zend_Validate_EmailAddress::DOT_ATOM, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is not a valid email address.', $label), - Zend_Validate_EmailAddress::QUOTED_STRING, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is not a valid email address.', $label), - Zend_Validate_EmailAddress::INVALID_LOCAL_PART, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" exceeds the allowed length.', $label), - Zend_Validate_EmailAddress::LENGTH_EXCEEDED, - ); - $validator->setMessage( - Mage::helper('customer')->__("'%value%' appears to be an IP address, but IP addresses are not allowed"), - Zend_Validate_Hostname::IP_ADDRESS_NOT_ALLOWED, - ); - $validator->setMessage( - Mage::helper('customer')->__("'%value%' appears to be a DNS hostname but cannot match TLD against known list"), - Zend_Validate_Hostname::UNKNOWN_TLD, - ); - $validator->setMessage( - Mage::helper('customer')->__("'%value%' appears to be a DNS hostname but contains a dash in an invalid position"), - Zend_Validate_Hostname::INVALID_DASH, - ); - $validator->setMessage( - Mage::helper('customer')->__("'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'"), - Zend_Validate_Hostname::INVALID_HOSTNAME_SCHEMA, - ); - $validator->setMessage( - Mage::helper('customer')->__("'%value%' appears to be a DNS hostname but cannot extract TLD part"), - Zend_Validate_Hostname::UNDECIPHERABLE_TLD, - ); - $validator->setMessage( - Mage::helper('customer')->__("'%value%' does not appear to be a valid local network name"), - Zend_Validate_Hostname::INVALID_LOCAL_NAME, - ); - $validator->setMessage( - Mage::helper('customer')->__("'%value%' appears to be a local network name but local network names are not allowed"), - Zend_Validate_Hostname::LOCAL_NAME_NOT_ALLOWED, - ); - $validator->setMessage( - Mage::helper('customer')->__("'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded"), - Zend_Validate_Hostname::CANNOT_DECODE_PUNYCODE, - ); - if (!$validator->isValid($value)) { - return array_unique($validator->getMessages()); + if ($validator->validateEmail(value: $value)->count() > 0) { + return [Mage::helper('eav')->__('"%s" is not a valid email address.', $label)]; } break; case 'url': $parsedUrl = parse_url($value); + $message = Mage::helper('eav')->__('"%s" is not a valid URL.', $label); if ($parsedUrl === false || empty($parsedUrl['scheme']) || empty($parsedUrl['host'])) { - return [Mage::helper('eav')->__('"%s" is not a valid URL.', $label)]; + return [$message]; } - $validator = new Zend_Validate_Hostname(); - if (!$validator->isValid($parsedUrl['host'])) { - return [Mage::helper('eav')->__('"%s" is not a valid URL.', $label)]; + $violations = $validator->validate(value: $value, constraints: [new Constraints\Hostname()]); + if ($violations->count() > 0) { + return [$message]; } break; case 'date': - $validator = new Zend_Validate_Date(Varien_Date::DATE_INTERNAL_FORMAT); - $validator->setMessage( - Mage::helper('eav')->__('"%s" invalid type entered.', $label), - Zend_Validate_Date::INVALID, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" is not a valid date.', $label), - Zend_Validate_Date::INVALID_DATE, - ); - $validator->setMessage( - Mage::helper('eav')->__('"%s" does not fit the entered date format.', $label), - Zend_Validate_Date::FALSEFORMAT, - ); - if (!$validator->isValid($value)) { - return array_unique($validator->getMessages()); + if ($validator->validateDate(value: $value)->count() > 0) { + return [Mage::helper('eav')->__('"%s" is not a valid date.', $label)]; } break; diff --git a/app/code/core/Mage/Eav/Model/Entity/Attribute.php b/app/code/core/Mage/Eav/Model/Entity/Attribute.php index 6f28426f5e1..12dd5e87639 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Attribute.php +++ b/app/code/core/Mage/Eav/Model/Entity/Attribute.php @@ -125,18 +125,19 @@ public function loadEntityAttributeIdBySet() * * @inheritDoc * @throws Mage_Eav_Exception + * @throws Mage_Core_Exception */ protected function _beforeSave() { - /** + /* * Check for maximum attribute_code length */ + $validator = $this->getValidationHelper(); if (isset($this->_data['attribute_code']) - && !Zend_Validate::is( - $this->_data['attribute_code'], - 'StringLength', - ['max' => self::ATTRIBUTE_CODE_MAX_LENGTH], - ) + && $validator->validateLength( + value: $this->_data['attribute_code'], + max: self::ATTRIBUTE_CODE_MAX_LENGTH, + )->count() > 0 ) { throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Maximum length of attribute code must be less then %s symbols', self::ATTRIBUTE_CODE_MAX_LENGTH)); } diff --git a/app/code/core/Mage/Eav/Model/Entity/Setup.php b/app/code/core/Mage/Eav/Model/Entity/Setup.php index 88db13a6d76..b38632164fc 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Setup.php +++ b/app/code/core/Mage/Eav/Model/Entity/Setup.php @@ -638,6 +638,7 @@ protected function _prepareValues($attr) * Validate attribute data before insert into table * * @param array $data + * @throws Mage_Core_Exception * @throws Mage_Eav_Exception * @return true */ @@ -645,8 +646,14 @@ protected function _validateAttributeData($data) { $attributeCodeMaxLength = Mage_Eav_Model_Entity_Attribute::ATTRIBUTE_CODE_MAX_LENGTH; + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if (isset($data['attribute_code']) - && !Zend_Validate::is($data['attribute_code'], 'StringLength', ['max' => $attributeCodeMaxLength]) + && $validator->validateLength( + value: $data['attribute_code'], + max: $attributeCodeMaxLength, + )->count() > 0 ) { throw Mage::exception( 'Mage_Eav', diff --git a/app/code/core/Mage/ImportExport/Model/Import/Entity/Customer.php b/app/code/core/Mage/ImportExport/Model/Import/Entity/Customer.php index ddb1b5b27bb..b2d4e0f53cc 100644 --- a/app/code/core/Mage/ImportExport/Model/Import/Entity/Customer.php +++ b/app/code/core/Mage/ImportExport/Model/Import/Entity/Customer.php @@ -640,7 +640,9 @@ public function validateRow(array $rowData, $rowNum) $this->addRowError(self::ERROR_EMAIL_SITE_NOT_FOUND, $rowNum); } } elseif (self::SCOPE_DEFAULT == $rowScope) { // row is SCOPE_DEFAULT = new customer block begins - if (!Zend_Validate::is($email, 'EmailAddress')) { + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if ($validator->validateEmail($email)->count() > 0) { $this->addRowError(self::ERROR_INVALID_EMAIL, $rowNum); } elseif (!isset($this->_websiteCodeToId[$website])) { $this->addRowError(self::ERROR_INVALID_WEBSITE, $rowNum); diff --git a/app/code/core/Mage/Newsletter/Model/Template.php b/app/code/core/Mage/Newsletter/Model/Template.php index 43f74f97f5b..c46fed7d655 100644 --- a/app/code/core/Mage/Newsletter/Model/Template.php +++ b/app/code/core/Mage/Newsletter/Model/Template.php @@ -73,31 +73,41 @@ protected function _construct() */ public function validate() { - $validators = [ - 'template_code' => [Zend_Filter_Input::ALLOW_EMPTY => false], - 'template_type' => 'Int', - 'template_sender_email' => 'EmailAddress', - 'template_sender_name' => [Zend_Filter_Input::ALLOW_EMPTY => false], - ]; - $data = []; - foreach (array_keys($validators) as $validateField) { - $data[$validateField] = $this->getDataUsingMethod($validateField); - } - - $validateInput = new Zend_Filter_Input([], $validators, $data); - if (!$validateInput->isValid()) { - $errorMessages = []; - foreach ($validateInput->getMessages() as $messages) { - if (is_array($messages)) { - foreach ($messages as $message) { - $errorMessages[] = $message; - } - } else { - $errorMessages[] = $messages; - } - } - - Mage::throwException(implode("\n", $errorMessages)); + $validator = $this->getValidationHelper(); + $violations = new ArrayObject(); + + $violations->append($validator->validateNotEmpty( + value: $this->getDataUsingMethod('template_code'), + message: "You must give a non-empty value for field 'template_code'", + )); + + $message = "You must give a non-empty value for field 'template_type'"; + $templateType = $this->getDataUsingMethod('template_type'); + + $violations->append($validator->validateNotEmpty( + value: $templateType, + message: $message, + )); + + $violations->append($validator->validateType( + value: $templateType, + type: 'int', + message: $message, + )); + + $violations->append($validator->validateEmail( + value: $this->getDataUsingMethod('template_sender_email'), + message: "You must give a non-empty value for field 'template_sender_email'", + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getDataUsingMethod('template_sender_name'), + message: "You must give a non-empty value for field 'template_sender_name'", + )); + + $errors = $validator->getErrorMessages($violations); + if ($errors) { + Mage::throwException(implode("\n", iterator_to_array($errors))); } } @@ -105,6 +115,7 @@ public function validate() * Processing object before save data * * @inheritDoc + * @throws Mage_Core_Exception */ protected function _beforeSave() { @@ -275,7 +286,8 @@ public function getMail() * @param array $variables template variables * @param null|string $name receiver name (if subscriber model not specified) * @param null|Mage_Newsletter_Model_Queue $queue queue model, used for problems reporting - * @return bool + * @throws Exception|Throwable + * @return bool * @deprecated since 1.4.0.1 **/ public function send($subscriber, array $variables = [], $name = null, ?Mage_Newsletter_Model_Queue $queue = null) @@ -371,6 +383,7 @@ public function send($subscriber, array $variables = [], $name = null, ?Mage_New /** * Prepare Process (with save) * + * @throws Throwable * @return $this * @deprecated since 1.4.0.1 */ @@ -385,6 +398,7 @@ public function preprocess() /** * Retrieve processed template subject * + * @throws Exception * @return string */ public function getProcessedTemplateSubject(array $variables) diff --git a/app/code/core/Mage/Newsletter/controllers/SubscriberController.php b/app/code/core/Mage/Newsletter/controllers/SubscriberController.php index 96182e3c281..032195a86b4 100644 --- a/app/code/core/Mage/Newsletter/controllers/SubscriberController.php +++ b/app/code/core/Mage/Newsletter/controllers/SubscriberController.php @@ -34,8 +34,11 @@ public function newAction() $customerSession = Mage::getSingleton('customer/session'); $email = (string) $this->getRequest()->getPost('email'); + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + try { - if (!Zend_Validate::is($email, 'EmailAddress')) { + if ($validator->validateEmail($email)->count() > 0) { Mage::throwException($this->__('Please enter a valid email address.')); } diff --git a/app/code/core/Mage/Oauth/Model/Consumer.php b/app/code/core/Mage/Oauth/Model/Consumer.php index 39b9888961a..c98c7b18b23 100644 --- a/app/code/core/Mage/Oauth/Model/Consumer.php +++ b/app/code/core/Mage/Oauth/Model/Consumer.php @@ -70,25 +70,21 @@ protected function _beforeSave() /** * Validate data * + * @throws Mage_Core_Exception Throw exception on fail validation * @return bool - * @throw Mage_Core_Exception|Exception Throw exception on fail validation */ public function validate() { - /** @var Mage_Oauth_Model_Consumer_Validator_KeyLength $validatorLength */ - $validatorLength = Mage::getModel('oauth/consumer_validator_keyLength', ['length' => self::KEY_LENGTH]); + $validator = $this->getValidationHelper(); - $validatorLength->setName('Consumer Key'); - if (!$validatorLength->isValid($this->getKey())) { - $messages = $validatorLength->getMessages(); - Mage::throwException(array_shift($messages)); + $violations = $validator->validateLength(value: $this->getKey(), exactly: self::KEY_LENGTH); + if ($violations->count() > 0) { + Mage::throwException($violations->get(0)->getMessage()); } - $validatorLength->setLength(self::SECRET_LENGTH); - $validatorLength->setName('Consumer Secret'); - if (!$validatorLength->isValid($this->getSecret())) { - $messages = $validatorLength->getMessages(); - Mage::throwException(array_shift($messages)); + $violations = $validator->validateLength(value: $this->getSecret(), exactly: self::SECRET_LENGTH); + if ($violations->count() > 0) { + Mage::throwException($violations->get(0)->getMessage()); } return true; diff --git a/app/code/core/Mage/Oauth/Model/Consumer/Validator/KeyLength.php b/app/code/core/Mage/Oauth/Model/Consumer/Validator/KeyLength.php index 884a3ec2439..bc452e0bb0c 100644 --- a/app/code/core/Mage/Oauth/Model/Consumer/Validator/KeyLength.php +++ b/app/code/core/Mage/Oauth/Model/Consumer/Validator/KeyLength.php @@ -12,59 +12,16 @@ * * @package Mage_Oauth */ -class Mage_Oauth_Model_Consumer_Validator_KeyLength extends Zend_Validate_StringLength +class Mage_Oauth_Model_Consumer_Validator_KeyLength extends Mage_Core_Helper_Validate_Abstract { /** * Key name - * - * @var string */ - protected $_name = 'Key'; + protected string $name = 'Key'; - /** - * Sets validator options - * - * @param array|int|Zend_Config $options - */ - public function __construct($options = []) - { - $args = func_get_args(); - if (!is_array($options)) { - $options = $args; - if (!isset($options[1])) { - $options[1] = 'utf-8'; - } + protected ?int $max = null; - parent::__construct($options[0], $options[0], $options[1]); - return; - } else { - if (isset($options['length'])) { - $options['max'] - = $options['min'] = $options['length']; - } - - if (isset($options['name'])) { - $this->_name = $options['name']; - } - } - - parent::__construct($options); - } - - /** - * Init validation failure message template definitions - * - * @return $this - */ - protected function _initMessageTemplates() - { - $_messageTemplates[self::TOO_LONG] - = Mage::helper('oauth')->__("%name% '%value%' is too long. It must has length %min% symbols."); - $_messageTemplates[self::TOO_SHORT] - = Mage::helper('oauth')->__("%name% '%value%' is too short. It must has length %min% symbols."); - - return $this; - } + protected ?int $min = null; /** * Additional variables available for validation failure messages @@ -72,51 +29,63 @@ protected function _initMessageTemplates() * @var array */ protected $_messageVariables = [ - 'min' => '_min', - 'max' => '_max', - 'name' => '_name', + 'min' => 'min', + 'max' => 'max', + 'name' => 'name', ]; /** - * Set length + * Sets validator options * - * @param int $length - * @return $this + * @param array $options */ - public function setLength($length) + public function __construct($options = []) { - parent::setMax($length); - parent::setMin($length); - return $this; - } + if (isset($options['length'])) { + $this->min = $this->max = $options['length']; + } - /** - * Set length - * - * @return int - */ - public function getLength() - { - return parent::getMin(); + if (isset($options['name'])) { + $this->name = $options['name']; + } } /** - * Defined by Zend_Validate_Interface + * Defined by Mage_Validation_Interface * * Returns true if and only if the string length of $value is at least the min option and * no greater than the max option (when the max option is not null). * * @param string $value + * @throws Mage_Core_Exception * @return bool */ public function isValid($value) { - $result = parent::isValid($value); - if (!$result && isset($this->_messages[self::INVALID])) { - throw new Exception($this->_messages[self::INVALID]); + $this->_setValue($value); + + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + $violation = $validator->validateLength( + value: $value, + min: $this->min, + max: $this->max, + exactMessage: $this->createMessageFromTemplate( + Mage::helper('oauth')->__("%name% '%value%' should have exactly %min% symbols."), + ), + minMessage: $this->createMessageFromTemplate( + Mage::helper('oauth')->__("%name% '%value%' is too short. It must has length %min% symbols."), + ), + maxMessage: $this->createMessageFromTemplate( + Mage::helper('oauth')->__("%name% '%value%' is too long. It must has length %max% symbols."), + ), + ); + + if ($violation->count() > 0) { + throw new Mage_Core_Exception($violation->get(0)->getMessage()); } - return $result; + return true; } /** @@ -127,7 +96,7 @@ public function isValid($value) */ public function setName($name) { - $this->_name = $name; + $this->name = $name; return $this; } @@ -138,6 +107,6 @@ public function setName($name) */ public function getName() { - return $this->_name; + return $this->name; } } diff --git a/app/code/core/Mage/Oauth/Model/Server.php b/app/code/core/Mage/Oauth/Model/Server.php index f7e60d8d330..ad80c30af59 100644 --- a/app/code/core/Mage/Oauth/Model/Server.php +++ b/app/code/core/Mage/Oauth/Model/Server.php @@ -476,8 +476,11 @@ protected function _validateCallbackUrlParam() return; } + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + if (self::CALLBACK_ESTABLISHED !== $this->_protocolParams['oauth_callback'] - && !Zend_Uri::check($this->_protocolParams['oauth_callback']) + && $validator->validateUrl($this->_protocolParams['oauth_callback'])->count() > 0 ) { $this->_throwException('oauth_callback', self::ERR_PARAMETER_REJECTED); } diff --git a/app/code/core/Mage/Oauth/Model/Token.php b/app/code/core/Mage/Oauth/Model/Token.php index 11ef546f23e..598bab727e8 100644 --- a/app/code/core/Mage/Oauth/Model/Token.php +++ b/app/code/core/Mage/Oauth/Model/Token.php @@ -224,45 +224,42 @@ protected function _beforeSave() /** * Validate data * + * @throws Mage_Core_Exception Throw exception on fail validation * @return bool - * @throw Mage_Core_Exception|Exception Throw exception on fail validation */ public function validate() { - if (Mage_Oauth_Model_Server::CALLBACK_ESTABLISHED !== $this->getCallbackUrl()) { + $validator = $this->getValidationHelper(); + + $callback = $this->getCallbackUrl(); + + if (Mage_Oauth_Model_Server::CALLBACK_ESTABLISHED !== $callback) { $callbackUrl = $this->getConsumer()->getCallbackUrl(); - $isWhitelisted = $callbackUrl && str_starts_with($this->getCallbackUrl(), $callbackUrl); - $validatorUrl = Mage::getSingleton('core/url_validator'); - if (!$isWhitelisted && !$validatorUrl->isValid($this->getCallbackUrl())) { - $messages = $validatorUrl->getMessages(); - Mage::throwException(array_shift($messages)); + $isWhitelisted = $callbackUrl && str_starts_with($callback, $callbackUrl); + $violations = $validator->validateUrl( + value: $callback, + message: 'Invalid URL {{ value }}.', + ); + if (!$isWhitelisted && $violations->count() > 0) { + Mage::throwException($violations->get(0)->getMessage()); } } - /** @var Mage_Oauth_Model_Consumer_Validator_KeyLength $validatorLength */ - $validatorLength = Mage::getModel( - 'oauth/consumer_validator_keyLength', - ); - $validatorLength->setLength(self::LENGTH_SECRET); - $validatorLength->setName('Token Secret Key'); - if (!$validatorLength->isValid($this->getSecret())) { - $messages = $validatorLength->getMessages(); - Mage::throwException(array_shift($messages)); + $violations = $validator->validateLength(value: $this->getSecret(), exactly: self::LENGTH_SECRET); + if ($violations->count() > 0) { + Mage::throwException($violations->get(0)->getMessage()); } - $validatorLength->setLength(self::LENGTH_TOKEN); - $validatorLength->setName('Token Key'); - if (!$validatorLength->isValid($this->getToken())) { - $messages = $validatorLength->getMessages(); - Mage::throwException(array_shift($messages)); + $violations = $validator->validateLength(value: $this->getToken(), exactly: self::LENGTH_TOKEN); + if ($violations->count() > 0) { + Mage::throwException($violations->get(0)->getMessage()); } - if (($verifier = $this->getVerifier()) !== null) { - $validatorLength->setLength(self::LENGTH_VERIFIER); - $validatorLength->setName('Verifier Key'); - if (!$validatorLength->isValid($verifier)) { - $messages = $validatorLength->getMessages(); - Mage::throwException(array_shift($messages)); + $verifier = $this->getVerifier(); + if ($verifier !== null) { + $violations = $validator->validateLength(value: $verifier, exactly: self::LENGTH_VERIFIER); + if ($violations->count() > 0) { + Mage::throwException($violations->get(0)->getMessage()); } } diff --git a/app/code/core/Mage/Review/Model/Review.php b/app/code/core/Mage/Review/Model/Review.php index 98d710adf68..19d5db98222 100644 --- a/app/code/core/Mage/Review/Model/Review.php +++ b/app/code/core/Mage/Review/Model/Review.php @@ -132,30 +132,34 @@ public function getReviewUrl() } /** - * @throws Zend_Validate_Exception - * @return array|bool + * @return array|true */ public function validate() { - $errors = []; - - if (!Zend_Validate::is($this->getTitle(), 'NotEmpty')) { - $errors[] = Mage::helper('review')->__("Review summary can't be empty"); - } - - if (!Zend_Validate::is($this->getNickname(), 'NotEmpty')) { - $errors[] = Mage::helper('review')->__("Nickname can't be empty"); - } - - if (!Zend_Validate::is($this->getDetail(), 'NotEmpty')) { - $errors[] = Mage::helper('review')->__("Review can't be empty"); - } - - if (empty($errors)) { + $validator = $this->getValidationHelper(); + $violations = new ArrayObject(); + + $violations->append($validator->validateNotEmpty( + value: $this->getTitle(), + message: Mage::helper('review')->__("Review summary can't be empty"), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getNickname(), + message: Mage::helper('review')->__("Nickname can't be empty"), + )); + + $violations->append($validator->validateNotEmpty( + value: $this->getDetail(), + message: Mage::helper('review')->__("Review can't be empty"), + )); + + $errors = $validator->getErrorMessages($violations); + if (!$errors) { return true; } - return $errors; + return (array) $errors; } /** @@ -173,6 +177,7 @@ protected function _afterDeleteCommit() * Append review summary to product collection * * @param Mage_Catalog_Model_Resource_Product_Collection $collection + * @throws Mage_Core_Model_Store_Exception * @return $this */ public function appendSummary($collection) @@ -225,6 +230,7 @@ public function isApproved() * Check if current review available on passed store * * @param int|Mage_Core_Model_Store $store + * @throws Mage_Core_Model_Store_Exception * @return bool */ public function isAvailableOnStore($store = null) diff --git a/app/code/core/Mage/Widget/Model/Widget/Instance.php b/app/code/core/Mage/Widget/Model/Widget/Instance.php index c5c3f0c889c..d6097ecc4c8 100644 --- a/app/code/core/Mage/Widget/Model/Widget/Instance.php +++ b/app/code/core/Mage/Widget/Model/Widget/Instance.php @@ -178,7 +178,7 @@ protected function _beforeSave() /** * Validate widget instance data * - * @return bool|string + * @return string|true */ public function validate() { diff --git a/app/code/core/Mage/Wishlist/Model/Item.php b/app/code/core/Mage/Wishlist/Model/Item.php index 8fd00aedbb7..2b958a0d9d1 100644 --- a/app/code/core/Mage/Wishlist/Model/Item.php +++ b/app/code/core/Mage/Wishlist/Model/Item.php @@ -228,7 +228,7 @@ protected function _afterSave() * Validate wish list item data * * @throws Mage_Core_Exception - * @return bool + * @return true */ public function validate() { diff --git a/app/code/core/Mage/Wishlist/controllers/IndexController.php b/app/code/core/Mage/Wishlist/controllers/IndexController.php index e930de8efbc..87125029fcc 100644 --- a/app/code/core/Mage/Wishlist/controllers/IndexController.php +++ b/app/code/core/Mage/Wishlist/controllers/IndexController.php @@ -666,25 +666,37 @@ public function sendAction() $emails = array_filter(explode(',', $this->getRequest()->getPost('emails', ''))); $message = nl2br(htmlspecialchars((string) $this->getRequest()->getPost('message'))); - $error = false; - if (empty($emails)) { - $error = $this->__("Email address can't be empty."); - } elseif (count($emails) > 5) { - $error = $this->__('Please enter no more than 5 email addresses.'); - } else { - foreach ($emails as $index => $email) { - $email = trim($email); - if (!Zend_Validate::is($email, 'EmailAddress')) { - $error = $this->__('Please input a valid email address.'); - break; - } + $violations = new ArrayObject(); + + /** @var Mage_Core_Helper_Validate $validator */ + $validator = Mage::helper('core/validate'); + + $violations->append($validator->validateCount( + value: $emails, + min: 1, + max: 5, + minMessage: $this->__("Email address can't be empty."), + maxMessage: $this->__('Please enter no more than 5 email addresses.'), + )); + + foreach ($emails as $index => $email) { + $email = trim($email); + $violation = $validator->validateEmail( + value: $email, + message: $this->__('Please input a valid email address.'), + ); + + if ($violation->count() === 0) { $emails[$index] = $email; } + + $violations->append($violation); } - if ($error) { - Mage::getSingleton('wishlist/session')->addError($error); + $errors = $validator->getErrorMessages($violations); + if ($errors) { + Mage::getSingleton('wishlist/session')->addError(implode('
', array_unique(iterator_to_array($errors)))); Mage::getSingleton('wishlist/session')->setSharingForm($this->getRequest()->getPost()); $this->_redirect('*/*/share'); return; diff --git a/app/locale/en_US/Mage_Customer.csv b/app/locale/en_US/Mage_Customer.csv index 5278d291001..b4bc98f5a8e 100644 --- a/app/locale/en_US/Mage_Customer.csv +++ b/app/locale/en_US/Mage_Customer.csv @@ -276,6 +276,7 @@ "PDF","PDF" "Password","Password" "Password Management","Password Management" +"Password must include both numeric and alphabetic characters.","Password must include both numeric and alphabetic characters." "Password Options","Password Options" "Password forgotten","Password forgotten" "Password:","Password:" diff --git a/composer.json b/composer.json index ca6060e3e1a..9a657d468b6 100644 --- a/composer.json +++ b/composer.json @@ -41,12 +41,14 @@ "phpseclib/mcrypt_compat": "^2.0.3", "phpseclib/phpseclib": "^3.0.14", "shardj/zf1-future": "^1.24.1", + "symfony/mime": "^6.4", "symfony/polyfill-php82": "^1.33", "symfony/polyfill-php83": "^1.33", "symfony/polyfill-php84": "^1.33", "symfony/polyfill-php85": "^1.33", "symfony/string": "^6.4", "symfony/translation-contracts": "^3.5", + "symfony/validator": "^6.4", "tinymce/tinymce": "^8.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 426937ea60a..3b7156a9d4f 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": "17025e35a7b5f18113eb2defff3bfebf", + "content-hash": "3174a90f94799ede3d792b6e0f1e0f00", "packages": [ { "name": "carbonphp/carbon-doctrine-types", @@ -4333,6 +4333,107 @@ ], "time": "2024-09-27T08:32:26+00:00" }, + { + "name": "symfony/validator", + "version": "v6.4.27", + "source": { + "type": "git", + "url": "https://github.com/symfony/validator.git", + "reference": "60dd71e219cd3d76fde906eb6b6c1271db628f5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/validator/zipball/60dd71e219cd3d76fde906eb6b6c1271db628f5b", + "reference": "60dd71e219cd3d76fde906eb6b6c1271db628f5b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php83": "^1.27", + "symfony/translation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/annotations": "<1.13", + "doctrine/lexer": "<1.1", + "symfony/dependency-injection": "<5.4", + "symfony/expression-language": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/intl": "<5.4", + "symfony/property-info": "<5.4", + "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3|>=7.0,<7.0.3", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.13|^2", + "egulias/email-validator": "^2.1.10|^3|^4", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/property-access": "^5.4|^6.0|^7.0", + "symfony/property-info": "^5.4|^6.0|^7.0", + "symfony/translation": "^5.4.35|~6.3.12|^6.4.3|^7.0.3", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Validator\\": "" + }, + "exclude-from-classmap": [ + "/Tests/", + "/Resources/bin/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to validate values", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/validator/tree/v6.4.27" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-10-23T19:49:35+00:00" + }, { "name": "symfony/var-dumper", "version": "v6.4.25", @@ -9783,107 +9884,6 @@ ], "time": "2025-07-10T08:14:14+00:00" }, - { - "name": "symfony/validator", - "version": "v6.4.25", - "source": { - "type": "git", - "url": "https://github.com/symfony/validator.git", - "reference": "9352177c0e937793423053846f80bee805552324" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/9352177c0e937793423053846f80bee805552324", - "reference": "9352177c0e937793423053846f80bee805552324", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php83": "^1.27", - "symfony/translation-contracts": "^2.5|^3" - }, - "conflict": { - "doctrine/annotations": "<1.13", - "doctrine/lexer": "<1.1", - "symfony/dependency-injection": "<5.4", - "symfony/expression-language": "<5.4", - "symfony/http-kernel": "<5.4", - "symfony/intl": "<5.4", - "symfony/property-info": "<5.4", - "symfony/translation": "<5.4.35|>=6.0,<6.3.12|>=6.4,<6.4.3|>=7.0,<7.0.3", - "symfony/yaml": "<5.4" - }, - "require-dev": { - "doctrine/annotations": "^1.13|^2", - "egulias/email-validator": "^2.1.10|^3|^4", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/finder": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/intl": "^5.4|^6.0|^7.0", - "symfony/mime": "^5.4|^6.0|^7.0", - "symfony/property-access": "^5.4|^6.0|^7.0", - "symfony/property-info": "^5.4|^6.0|^7.0", - "symfony/translation": "^5.4.35|~6.3.12|^6.4.3|^7.0.3", - "symfony/yaml": "^5.4|^6.0|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Validator\\": "" - }, - "exclude-from-classmap": [ - "/Tests/", - "/Resources/bin/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools to validate values", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/validator/tree/v6.4.25" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-08-27T11:31:57+00:00" - }, { "name": "symfony/var-exporter", "version": "v6.4.25", diff --git a/cypress/e2e/openmage/frontend/customer/account.cy.js b/cypress/e2e/openmage/frontend/customer/account.cy.js index 2fbf105621a..1037c7d7443 100644 --- a/cypress/e2e/openmage/frontend/customer/account.cy.js +++ b/cypress/e2e/openmage/frontend/customer/account.cy.js @@ -42,12 +42,27 @@ describe('Checks customer account create', () => { validation.validateFields(test.create, validation.requiredEntry); }); - it('Submits valid form with random email', () => { - const email = cy.openmage.utils.generateRandomEmail(); - const firstname = 'John'; - const lastname = 'Doe'; + const email = cy.openmage.utils.generateRandomEmail(); + const firstname = 'John'; + const lastname = 'Doe'; + + it('Submits invalid form with weak password', () => { const password = '12345678'; + // see PR: https://github.com/OpenMage/magento-lts/pull/4617 + // const message = 'Thank you for registering with Madison Island.'; + const message = 'Password must include both numeric and alphabetic characters.'; + const filename = 'message.customer.account.create.invalid.weakpassword'; + cy.get(test.create.__fields.firstname._).type(firstname).should('have.value', firstname); + cy.get(test.create.__fields.lastname._).type(lastname).should('have.value', lastname); + cy.get(test.create.__fields.email_address._).type(email).should('have.value', email); + cy.get(test.create.__fields.password._).type(password).should('have.value', password); + cy.get(test.create.__fields.confirmation._).type(password).should('have.value', password); + tools.click(test._buttonSubmit); + validation.hasErrorMessage(message, { screenshot: false, filename: filename }); + }); + it('Submits invalid form', () => { + const password = 'veryl0ngpassw0rd'; // see PR: https://github.com/OpenMage/magento-lts/pull/4617 // const message = 'Thank you for registering with Madison Island.'; const message = 'Thank you for registering with ENV name default.'; diff --git a/lib/Varien/Convert/Adapter/Http/Curl.php b/lib/Varien/Convert/Adapter/Http/Curl.php index aa768a2fa9b..69e2ad35cc6 100644 --- a/lib/Varien/Convert/Adapter/Http/Curl.php +++ b/lib/Varien/Convert/Adapter/Http/Curl.php @@ -7,6 +7,9 @@ * @package Varien_Convert */ +use Symfony\Component\Validator\Constraints as Assert; +use Symfony\Component\Validator\Validation; + /** * Convert CURL HTTP adapter * @@ -21,7 +24,13 @@ public function load() $uri = $this->getVar('uri'); // validate input parameter - if (!Zend_Uri::check($uri)) { + $validator = Validation::createValidator(); + $violations = $validator->validate($uri, [ + new Assert\NotBlank(), + new Assert\Url(), + ]); + + if ($violations->count() > 0) { $this->addException("Expecting a valid 'uri' parameter"); } diff --git a/lib/Varien/File/Uploader.php b/lib/Varien/File/Uploader.php index 8692c09eacc..6f926d16de4 100644 --- a/lib/Varien/File/Uploader.php +++ b/lib/Varien/File/Uploader.php @@ -7,6 +7,9 @@ * @package Varien_File */ +use Symfony\Component\Validator\Validation; +use Symfony\Component\Validator\Constraints; + /** * File upload class * @@ -375,8 +378,10 @@ public function checkMimeType($validTypes = []) { try { if (count($validTypes) > 0) { - $validator = new Zend_Validate_File_MimeType($validTypes); - return $validator->isValid($this->_file['tmp_name']); + $validator = Validation::createValidator(); + return $validator->validate($this->_file['tmp_name'], [ + new Constraints\File(mimeTypes: $validTypes), + ])->count() === 0; } return true; diff --git a/tests/unit/Mage/Admin/Model/UserTest.php b/tests/unit/Mage/Admin/Model/UserTest.php index d22b9e34697..a89914f6f8e 100644 --- a/tests/unit/Mage/Admin/Model/UserTest.php +++ b/tests/unit/Mage/Admin/Model/UserTest.php @@ -12,6 +12,8 @@ namespace OpenMage\Tests\Unit\Mage\Admin\Model; use Mage; +use Mage_Admin_Model_Resource_User_Collection; +use Mage_Admin_Model_Roles; use Mage_Admin_Model_User as Subject; use Mage_Core_Exception; use OpenMage\Tests\Unit\OpenMageTest; @@ -29,6 +31,106 @@ public static function setUpBeforeClass(): void self::$subject = Mage::getModel('admin/user'); } + /** + * @group Model + */ + public function testSaveExtra(): void + { + $data = []; + self::assertInstanceOf(Subject::class, self::$subject->saveExtra($data)); + } + + /** + * @group Model + */ + public function testSaveRelations(): void + { + self::assertInstanceOf(Subject::class, self::$subject->saveRelations()); + } + + /** + * @group Model + */ + public function testGetRoles(): void + { + self::assertIsArray(self::$subject->getRoles()); + } + + /** + * @group Model + */ + public function testGetRole(): void + { + self::assertInstanceOf(Mage_Admin_Model_Roles::class, self::$subject->getRole()); + } + + /** + * @group Model + */ + public function testDeleteFromRole(): void + { + self::assertInstanceOf(Subject::class, self::$subject->deleteFromRole()); + } + + /** + * @group Model + */ + public function testRoleUserExists(): void + { + self::assertIsBool(self::$subject->roleUserExists()); + } + + /** + * @group Model + */ + public function testAdd(): void + { + self::assertInstanceOf(Subject::class, self::$subject->add()); + } + + /** + * @group Model + */ + public function testUserExists(): void + { + self::assertIsBool(self::$subject->userExists()); + } + + /** + * @group Model + */ + public function testGetCollection(): void + { + self::assertInstanceOf(Mage_Admin_Model_Resource_User_Collection::class, self::$subject->getCollection()); + } + + /** + * @group Model + */ + public function testSendNewPasswordEmail(): void + { + self::assertInstanceOf(Subject::class, self::$subject->sendNewPasswordEmail()); + } + + /** + * @group Model + */ + public function testGetUserId(): void + { + self::assertNull(self::$subject->getUserId()); + + self::$subject->setUserId(1); + self::assertIsInt(self::$subject->getUserId()); + } + + /** + * @group Model + */ + public function testGetAclRole(): void + { + self::assertStringStartsWith('U', self::$subject->getAclRole()); + } + /** * @dataProvider provideAuthenticateData * @group Model @@ -50,12 +152,40 @@ public function testAuthenticate(bool|string $expectedResult, array $methods): v } } + /** + * @group Model + */ + public function testHasAvailableResources() + { + self::assertIsBool(self::$subject->hasAvailableResources()); + } + + /** + * @group Model + * @group runInSeparateProcess + * @runInSeparateProcess + */ + public function testFindFirstAvailableMenu() + { + self::assertIsString(self::$subject->findFirstAvailableMenu()); + } + + /** + * @group Model + * @group runInSeparateProcess + * @runInSeparateProcess + */ + public function testGetStartupPageUrl() + { + self::assertIsString(self::$subject->getStartupPageUrl()); + } + /** * @dataProvider provideValidateAdminUserData * @param array|true $expectedResult * @group Model */ - public function testValidate($expectedResult, array $methods): void + public function testValidate(array|bool $expectedResult, array $methods): void { $mock = $this->getMockWithCalledMethods(Subject::class, $methods); @@ -63,8 +193,6 @@ public function testValidate($expectedResult, array $methods): void self::assertSame($expectedResult, $mock->validate()); } - - /** * @group Model */ @@ -74,6 +202,35 @@ public function testValidateCurrentPassword(): void self::assertIsArray(self::$subject->validateCurrentPassword('123')); } + /** + * @group Model + */ + public function testValidatePasswordHash(): void + { + self::assertIsBool(self::$subject->validatePasswordHash('a', 'b')); + } + + /** + * @group Model + * @group runInSeparateProcess + * @runInSeparateProcess + */ + public function testLogin(): void + { + self::assertInstanceOf(Subject::class, self::$subject->login('a', 'b')); + } + + + /** + * @group Model + * @group runInSeparateProcess + * @runInSeparateProcess + */ + public function testReload(): void + { + self::assertInstanceOf(Subject::class, self::$subject->reload()); + } + /** * @group Model */ @@ -139,24 +296,32 @@ public function testCleanPasswordsValidationData(): void /** * @group Model */ - public function testGetMinAdminPasswordLength(): void + public function testSendAdminNotification(): void { - $methods = ['getStoreConfigAsInt' => 10]; + $methods = ['getUserCreateAdditionalEmail' => ['test@example.com']]; $mock = $this->getMockWithCalledMethods(Subject::class, $methods); self::assertInstanceOf(Subject::class, $mock); - self::assertSame(14, $mock->getMinAdminPasswordLength()); + self::assertInstanceOf(Subject::class, $mock->sendAdminNotification(self::$subject)); } /** * @group Model */ - public function testSendAdminNotification(): void + public function testGetUserCreateAdditionalEmail(): void { - $methods = ['getUserCreateAdditionalEmail' => ['test@example.com']]; + self::assertSame([0 => ''], self::$subject->getUserCreateAdditionalEmail()); + } + + /** + * @group Model + */ + public function testGetMinAdminPasswordLength(): void + { + $methods = ['getStoreConfigAsInt' => 10]; $mock = $this->getMockWithCalledMethods(Subject::class, $methods); self::assertInstanceOf(Subject::class, $mock); - self::assertInstanceOf(Subject::class, $mock->sendAdminNotification(self::$subject)); + self::assertSame(14, $mock->getMinAdminPasswordLength()); } } diff --git a/tests/unit/Mage/Api/Model/UserTest.php b/tests/unit/Mage/Api/Model/UserTest.php index 05b647275bc..01a18d38463 100644 --- a/tests/unit/Mage/Api/Model/UserTest.php +++ b/tests/unit/Mage/Api/Model/UserTest.php @@ -13,7 +13,6 @@ use Mage_Api_Model_User as Subject; use OpenMage\Tests\Unit\OpenMageTest; use OpenMage\Tests\Unit\Traits\DataProvider\Mage\Api\Model\UserTrait; -use Zend_Validate_Exception; final class UserTest extends OpenMageTest { @@ -23,7 +22,6 @@ final class UserTest extends OpenMageTest * @dataProvider provideValidateApiUserData * @param array|true $expectedResult * @group Model - * @throws Zend_Validate_Exception */ public function testValidate($expectedResult, array $methods): void { diff --git a/tests/unit/Mage/Core/Model/Email/Template/AbstractTest.php b/tests/unit/Mage/Core/Model/Email/Template/AbstractTest.php index fef96b4c982..759d33374fe 100644 --- a/tests/unit/Mage/Core/Model/Email/Template/AbstractTest.php +++ b/tests/unit/Mage/Core/Model/Email/Template/AbstractTest.php @@ -25,7 +25,6 @@ final class AbstractTest extends OpenMageTest { use AbstractTrait; - /** @phpstan-ignore property.onlyWritten */ private static Subject $subject; public function setUp(): void @@ -45,8 +44,6 @@ public function testValidateFileExension(bool $expectedResult, string $filePath, self::assertFileDoesNotExist($filePath); } - self::markTestSkipped('wait...'); - /** @phpstan-ignore deadCode.unreachable */ self::assertSame($expectedResult, self::$subject->validateFileExension($filePath, $extension)); } } diff --git a/tests/unit/Mage/Core/Model/Layout/ValidatorTest.php b/tests/unit/Mage/Core/Model/Layout/ValidatorTest.php new file mode 100644 index 00000000000..3a8ed94d233 --- /dev/null +++ b/tests/unit/Mage/Core/Model/Layout/ValidatorTest.php @@ -0,0 +1,63 @@ +isValid($value)); + + if (!$expected) { + $messages = self::$subject->getMessages(); + self::assertIsArray($messages); + self::assertSame($expectedErrors, $messages); + } + } + + public function isValidDataProvider(): Generator + { + yield 'valid string' => [ + true, + 'default', + [], + ]; + + yield 'invalid string' => [ + false, + '', + [ + 'invalidXml' => 'XML data is invalid.', + ], + ]; + } +} diff --git a/tests/unit/Mage/Core/Model/Url/ValidatorTest.php b/tests/unit/Mage/Core/Model/Url/ValidatorTest.php new file mode 100644 index 00000000000..3d98a0946df --- /dev/null +++ b/tests/unit/Mage/Core/Model/Url/ValidatorTest.php @@ -0,0 +1,46 @@ +isValid($url)); + + if (!$expected) { + $messages = self::$subject->getMessages(); + self::assertIsArray($messages); + self::assertArrayHasKey(Subject::INVALID_URL, $messages); + self::assertStringContainsString($url, (string) $messages[Subject::INVALID_URL]); + } + } +} diff --git a/tests/unit/Mage/Customer/Model/CustomerTest.php b/tests/unit/Mage/Customer/Model/CustomerTest.php index 9e20f0caaca..14ccdaa83d5 100644 --- a/tests/unit/Mage/Customer/Model/CustomerTest.php +++ b/tests/unit/Mage/Customer/Model/CustomerTest.php @@ -14,7 +14,6 @@ use Mage_Customer_Model_Customer as Subject; use OpenMage\Tests\Unit\OpenMageTest; use OpenMage\Tests\Unit\Traits\DataProvider\Mage\Customer\CustomerTrait; -use Zend_Validate_Exception; final class CustomerTest extends OpenMageTest { @@ -24,7 +23,6 @@ final class CustomerTest extends OpenMageTest * @dataProvider provideValidateCustomerData * @param array|true $expectedResult * @throws Mage_Core_Exception - * @throws Zend_Validate_Exception */ public function testValidate($expectedResult, array $methods): void { diff --git a/tests/unit/Mage/GiftMessage/Helper/MessageTest.php b/tests/unit/Mage/GiftMessage/Helper/MessageTest.php index d77d74da3b7..4eb057326a5 100644 --- a/tests/unit/Mage/GiftMessage/Helper/MessageTest.php +++ b/tests/unit/Mage/GiftMessage/Helper/MessageTest.php @@ -31,8 +31,8 @@ public static function setUpBeforeClass(): void } /** + * @param 'address_item'|'config'|'item'|'items'|'order'|'order_item' $type Message type * @dataProvider provideIsMessagesAvailable - * * @group Helper */ public function testIsMessagesAvailable(string $type, Varien_Object $entity, null|bool|int|Mage_Core_Model_Store|string $store = null): void diff --git a/tests/unit/Mage/Log/Model/CustomerTest.php b/tests/unit/Mage/Log/Model/CustomerTest.php index 4d02658e367..9f390f2681d 100644 --- a/tests/unit/Mage/Log/Model/CustomerTest.php +++ b/tests/unit/Mage/Log/Model/CustomerTest.php @@ -26,7 +26,7 @@ final class CustomerTest extends OpenMageTest * @dataProvider loadByCustomerDataProvider * @group Model */ - public function testLoadByCustomer($input, $expectedCustomerId): void + public function testLoadByCustomer($input, int $expectedCustomerId): void { $mock = $this->getMockBuilder(Subject::class) ->onlyMethods(['load']) diff --git a/tests/unit/Mage/Newsletter/Model/TemplateTest.php b/tests/unit/Mage/Newsletter/Model/TemplateTest.php index dff18f877e6..3764b2aa25c 100644 --- a/tests/unit/Mage/Newsletter/Model/TemplateTest.php +++ b/tests/unit/Mage/Newsletter/Model/TemplateTest.php @@ -58,12 +58,12 @@ public function testValidate(?string $expected, array $methods): void public function validateTemplateDataProvider(): Generator { $validData = [ - 'setTemplateCode' => 'Valid Code', - 'setTemplateSenderEmail' => 'test@example.com', - 'setTemplateSenderName' => 'Sender Name', - 'setTemplateSubject' => 'Valid Subject', - 'setTemplateText' => 'Valid Template Text', - 'setTemplateType' => 1, + 'setTemplateCode' => 'Valid Code', + 'setTemplateSenderEmail' => 'test@example.com', + 'setTemplateSenderName' => 'Sender Name', + 'setTemplateSubject' => 'Valid Subject', + 'setTemplateText' => 'Valid Template Text', + 'setTemplateType' => 1, ]; yield 'valid data' => [ @@ -88,7 +88,7 @@ public function validateTemplateDataProvider(): Generator $data = $validData; $data['setTemplateSenderEmail'] = 'invalid-email'; yield 'invalid sender email' => [ - "'invalid-email' is not a valid email address in the basic format local-part@hostname", + "You must give a non-empty value for field 'template_sender_email'", $data, ]; diff --git a/tests/unit/Mage/Oauth/Model/ConsumerTest.php b/tests/unit/Mage/Oauth/Model/ConsumerTest.php new file mode 100644 index 00000000000..abfba604fd7 --- /dev/null +++ b/tests/unit/Mage/Oauth/Model/ConsumerTest.php @@ -0,0 +1,95 @@ +getMockWithCalledMethods(Subject::class, $methods); + + self::assertInstanceOf(Subject::class, $mock); + + try { + self::assertTrue($mock->validate()); + } catch (Mage_Core_Exception $mageCoreException) { + self::assertSame($expected, $mageCoreException->getMessage()); + } + } + + public function validateDataProvider(): Generator + { + $validData = [ + 'setKey' => str_repeat('x', 32), + 'setSecret' => str_repeat('x', 32), + ]; + + $error = 'This value should have exactly 32 characters.'; + + yield 'valid' => [ + true, + $validData, + ]; + + $data = $validData; + $data['setKey'] = str_repeat('x', 3); + yield 'invalid to short key' => [ + $error, + $data, + ]; + + $data = $validData; + $data['setKey'] = str_repeat('x', 33); + yield 'invalid to long key' => [ + $error, + $data, + ]; + + $data = $validData; + $data['setSecret'] = str_repeat('x', 3); + yield 'invalid to short secret' => [ + $error, + $data, + ]; + + $data = $validData; + $data['setSecret'] = str_repeat('x', 33); + yield 'invalid to long secret' => [ + $error, + $data, + ]; + } +} diff --git a/tests/unit/Mage/Oauth/Model/TokenTest.php b/tests/unit/Mage/Oauth/Model/TokenTest.php new file mode 100644 index 00000000000..d56d48f2dd8 --- /dev/null +++ b/tests/unit/Mage/Oauth/Model/TokenTest.php @@ -0,0 +1,106 @@ +setConsumerId($methods['setConsumerId']); + self::$subject->setCallbackUrl($methods['setCallbackUrl']); + self::$subject->setSecret($methods['setSecret']); + self::$subject->setToken($methods['setToken']); + self::$subject->setVerifier($methods['setVerifier']); + + try { + self::assertTrue(self::$subject->validate()); + } catch (Mage_Core_Exception $mageCoreException) { + self::assertSame($expected, $mageCoreException->getMessage()); + } + } + + public function validateDataProvider(): Generator + { + $validData = [ + 'setConsumerId' => '1', + 'setCallbackUrl' => 'https://example.com/callback', + 'setSecret' => str_repeat('x', 32), + 'setToken' => str_repeat('x', 32), + 'setVerifier' => str_repeat('x', 32), + ]; + + $error = 'This value should have exactly 32 characters.'; + + yield 'valid' => [ + true, + $validData, + ]; + + $data = $validData; + $data['setSecret'] = str_repeat('x', 3); + yield 'invalid to short secret' => [ + $error, + $data, + ]; + + $data = $validData; + $data['setSecret'] = str_repeat('x', 33); + yield 'invalid to long secret' => [ + $error, + $data, + ]; + + $data = $validData; + $data['setToken'] = str_repeat('x', 3); + yield 'invalid to short token' => [ + $error, + $data, + ]; + + $data = $validData; + $data['setToken'] = str_repeat('x', 33); + yield 'invalid to long token' => [ + $error, + $data, + ]; + + $data = $validData; + $data['setCallbackUrl'] = 'invalid-url'; + yield 'invalid url' => [ + 'Invalid URL "invalid-url".', + $data, + ]; + } +} diff --git a/tests/unit/Mage/Reports/Helper/DataTest.php b/tests/unit/Mage/Reports/Helper/DataTest.php index fb7ca21e221..b22ad811e04 100644 --- a/tests/unit/Mage/Reports/Helper/DataTest.php +++ b/tests/unit/Mage/Reports/Helper/DataTest.php @@ -49,6 +49,7 @@ public function testIsReportsEnabled(): void } /** + * @param 'day'|'month'|'year' $period Period type * @covers Mage_Reports_Helper_Data::getIntervals() * @dataProvider provideReportsDateIntervals * @group Helper diff --git a/tests/unit/Mage/Review/Model/ReviewTest.php b/tests/unit/Mage/Review/Model/ReviewTest.php index 5a0e5f843aa..7692bcc0630 100644 --- a/tests/unit/Mage/Review/Model/ReviewTest.php +++ b/tests/unit/Mage/Review/Model/ReviewTest.php @@ -19,7 +19,6 @@ use Mage_Review_Model_Review as Subject; use OpenMage\Tests\Unit\OpenMageTest; use OpenMage\Tests\Unit\Traits\DataProvider\Mage\Review\ReviewTrait; -use Zend_Validate_Exception; final class ReviewTest extends OpenMageTest { @@ -29,7 +28,6 @@ final class ReviewTest extends OpenMageTest * @dataProvider provideValidateReviewData * @param array|true $expectedResult * @group Model - * @throws Zend_Validate_Exception */ public function testValidate($expectedResult, array $methods): void { diff --git a/tests/unit/Traits/DataProvider/Base/UrlTrait.php b/tests/unit/Traits/DataProvider/Base/UrlTrait.php new file mode 100644 index 00000000000..3274c6f50df --- /dev/null +++ b/tests/unit/Traits/DataProvider/Base/UrlTrait.php @@ -0,0 +1,34 @@ + [ + false, + '', + ]; + + yield 'invalid wrong' => [ + false, + 'no-url', + ]; + + yield 'valid' => [ + true, + 'https://example.com', + ]; + } +} diff --git a/tests/unit/Traits/DataProvider/Mage/Admin/Model/BlockTrait.php b/tests/unit/Traits/DataProvider/Mage/Admin/Model/BlockTrait.php index a8b13e78eb6..e68c11430ca 100644 --- a/tests/unit/Traits/DataProvider/Mage/Admin/Model/BlockTrait.php +++ b/tests/unit/Traits/DataProvider/Mage/Admin/Model/BlockTrait.php @@ -35,7 +35,6 @@ public function provideValidateAdminBlockData(): Generator yield 'errors: blank blockname' => [ [ 'Block Name is required field.', - 'Block Name is incorrect.', 'Is Allowed is required field.', ], [ @@ -60,7 +59,6 @@ public function provideValidateAdminBlockData(): Generator yield 'errors: null blockname' => [ [ 'Block Name is required field.', - $errorIncorrectBlockName, ], [ 'getBlockName' => null, diff --git a/tests/unit/Traits/DataProvider/Mage/Admin/Model/UserTrait.php b/tests/unit/Traits/DataProvider/Mage/Admin/Model/UserTrait.php index 3b07b2281eb..9c8920a884f 100644 --- a/tests/unit/Traits/DataProvider/Mage/Admin/Model/UserTrait.php +++ b/tests/unit/Traits/DataProvider/Mage/Admin/Model/UserTrait.php @@ -11,6 +11,7 @@ namespace OpenMage\Tests\Unit\Traits\DataProvider\Mage\Admin\Model; use Generator; +use Varien_Date; trait UserTrait { @@ -61,7 +62,7 @@ public function provideAuthenticateData(): Generator public function provideValidateAdminUserData(): Generator { - yield 'fail #1' => [ + yield 'fail different passwords' => [ [ 0 => 'User Name is required field.', 1 => 'First Name is required field.', @@ -97,18 +98,18 @@ public function provideValidateAdminUserData(): Generator public function provideIsResetPasswordLinkTokenExpiredData(): Generator { - yield '#1' => [ + yield 'empty data' => [ true, [ 'getRpToken' => '', 'getRpTokenCreatedAt' => '', ], ]; - yield '#2' => [ + yield '#valid data' => [ true, [ 'getRpToken' => '1', - 'getRpTokenCreatedAt' => '0', + 'getRpTokenCreatedAt' => '2025-01-01 10:20:30', ], ]; } diff --git a/tests/unit/Traits/DataProvider/Mage/Adminhtml/Helper/ConfigTrait.php b/tests/unit/Traits/DataProvider/Mage/Adminhtml/Helper/ConfigTrait.php index 2a435d4a126..45d3188e83c 100644 --- a/tests/unit/Traits/DataProvider/Mage/Adminhtml/Helper/ConfigTrait.php +++ b/tests/unit/Traits/DataProvider/Mage/Adminhtml/Helper/ConfigTrait.php @@ -14,7 +14,7 @@ trait ConfigTrait { - public static $backendModel = [ + public static array $backendModel = [ 'color' => 'adminhtml/system_config_backend_color', ]; diff --git a/tests/unit/Traits/DataProvider/Mage/Api/Model/UserTrait.php b/tests/unit/Traits/DataProvider/Mage/Api/Model/UserTrait.php index 9b5fa081951..738d54ce593 100644 --- a/tests/unit/Traits/DataProvider/Mage/Api/Model/UserTrait.php +++ b/tests/unit/Traits/DataProvider/Mage/Api/Model/UserTrait.php @@ -80,7 +80,6 @@ public function provideValidateApiUserData(): Generator yield 'missing api key' => [ [ $errorLength, - $errorAlphaNumeric, $errorIdentical, ], $data, diff --git a/tests/unit/Traits/DataProvider/Mage/Cms/Block/Widget/Page/LinkTrait.php b/tests/unit/Traits/DataProvider/Mage/Cms/Block/Widget/Page/LinkTrait.php index 5dd99e3d1b2..2a442fb3dc8 100644 --- a/tests/unit/Traits/DataProvider/Mage/Cms/Block/Widget/Page/LinkTrait.php +++ b/tests/unit/Traits/DataProvider/Mage/Cms/Block/Widget/Page/LinkTrait.php @@ -15,12 +15,12 @@ trait LinkTrait { - public static $defaults = [ + public static array $defaults = [ 'custom_title' => 'Custom Title', 'custom_text' => 'Custom Text', ]; - public static $tests = [ + public static array $tests = [ 'empty' => 'empty', 'href' => 'href is set', 'no_data' => 'no data is set', diff --git a/tests/unit/Traits/DataProvider/Mage/Customer/CustomerTrait.php b/tests/unit/Traits/DataProvider/Mage/Customer/CustomerTrait.php index 43db8f21308..4518d3e71c5 100644 --- a/tests/unit/Traits/DataProvider/Mage/Customer/CustomerTrait.php +++ b/tests/unit/Traits/DataProvider/Mage/Customer/CustomerTrait.php @@ -75,7 +75,10 @@ public function provideValidateCustomerData(): Generator $data['getPassword'] = $password; $data['getPasswordConfirmation'] = $password; yield 'passwords to short' => [ - ['The minimum password length is 7'], + [ + 'The minimum password length is 7', + 'Password must include both numeric and alphabetic characters.', + ], $data, ]; @@ -84,7 +87,10 @@ public function provideValidateCustomerData(): Generator $data['getPassword'] = $password; $data['getPasswordConfirmation'] = $password; yield 'passwords to long' => [ - ['Please enter a password with at most 256 characters.'], + [ + 'Please enter a password with at most 256 characters.', + 'Password must include both numeric and alphabetic characters.', + ], $data, ]; @@ -92,7 +98,7 @@ public function provideValidateCustomerData(): Generator $data['getDob'] = ''; $data['shouldValidateDob'] = true; yield 'missing dob' => [ - true, # ['The Date of Birth is required.'], + ['The Date of Birth is required.'], $data, ]; @@ -100,7 +106,7 @@ public function provideValidateCustomerData(): Generator $data['getDob'] = 'abc'; $data['shouldValidateDob'] = true; yield 'invalid dob' => [ - true, # ['This value is not a valid date.'], + ['The Date of Birth is required.'], $data, ]; @@ -108,7 +114,7 @@ public function provideValidateCustomerData(): Generator $data['getTaxvat'] = ''; $data['shouldValidateTaxvat'] = true; yield 'missing taxvat' => [ - true, # ['The TAX/VAT number is required.'], + ['The TAX/VAT number is required.'], $data, ]; @@ -116,7 +122,7 @@ public function provideValidateCustomerData(): Generator $data['getGender'] = ''; $data['shouldValidateGender'] = true; yield 'missing gender' => [ - true, # ['Gender is required.'], + ['Gender is required.'], $data, ]; }