Skip to content

Commit dbcc3c4

Browse files
author
Oleksii Korshenko
authored
Merge pull request #1673 from magento-engcom/2.2-develop-prs
Public Pull Requests #11879 #4004: Newsletter Subscriber create-date not set, and change_status_at broken by @nemesis-back #11556 Fix #10583: Checkout place order exception when using a new address by @joni-jones Fixed Public Issues #4004 Newsletter Subscriber create-date not set, and change_status_at broken #10583 Checkout place order exception when using a new address
2 parents c896f2c + 5109d8c commit dbcc3c4

File tree

7 files changed

+165
-23
lines changed

7 files changed

+165
-23
lines changed

app/code/Magento/Newsletter/Model/Subscriber.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
use Magento\Customer\Api\AccountManagementInterface;
99
use Magento\Customer\Api\CustomerRepositoryInterface;
10+
use Magento\Framework\App\ObjectManager;
1011
use Magento\Framework\Exception\MailException;
1112
use Magento\Framework\Exception\NoSuchEntityException;
13+
use Magento\Framework\Stdlib\DateTime\DateTime;
1214

1315
/**
1416
* Subscriber model
@@ -94,6 +96,12 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel
9496
*/
9597
protected $_customerSession;
9698

99+
/**
100+
* Date
101+
* @var DateTime
102+
*/
103+
private $dateTime;
104+
97105
/**
98106
* Store manager
99107
*
@@ -134,9 +142,10 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel
134142
* @param CustomerRepositoryInterface $customerRepository
135143
* @param AccountManagementInterface $customerAccountManagement
136144
* @param \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation
137-
* @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource
138-
* @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
145+
* @param \Magento\Framework\Model\ResourceModel\AbstractResource|null $resource
146+
* @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
139147
* @param array $data
148+
* @param DateTime|null $dateTime
140149
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
141150
*/
142151
public function __construct(
@@ -152,7 +161,8 @@ public function __construct(
152161
\Magento\Framework\Translate\Inline\StateInterface $inlineTranslation,
153162
\Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
154163
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
155-
array $data = []
164+
array $data = [],
165+
DateTime $dateTime = null
156166
) {
157167
$this->_newsletterData = $newsletterData;
158168
$this->_scopeConfig = $scopeConfig;
@@ -162,6 +172,7 @@ public function __construct(
162172
$this->customerRepository = $customerRepository;
163173
$this->customerAccountManagement = $customerAccountManagement;
164174
$this->inlineTranslation = $inlineTranslation;
175+
$this->dateTime = $dateTime ?: ObjectManager::getInstance()->get(DateTime::class);
165176
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
166177
}
167178

@@ -810,4 +821,18 @@ public function getSubscriberFullName()
810821
}
811822
return $name;
812823
}
824+
825+
/**
826+
* Set date of last changed status
827+
*
828+
* @return $this
829+
*/
830+
public function beforeSave()
831+
{
832+
parent::beforeSave();
833+
if ($this->dataHasChangedFor('subscriber_status')) {
834+
$this->setChangeStatusAt($this->dateTime->gmtDate());
835+
}
836+
return $this;
837+
}
813838
}

app/code/Magento/Quote/Model/Quote.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,7 @@ public function addCustomerAddress(\Magento\Customer\Api\Data\AddressInterface $
10571057
public function updateCustomerData(\Magento\Customer\Api\Data\CustomerInterface $customer)
10581058
{
10591059
$quoteCustomer = $this->getCustomer();
1060-
$this->dataObjectHelper->mergeDataObjects(get_class($quoteCustomer), $quoteCustomer, $customer);
1060+
$this->dataObjectHelper->mergeDataObjects(CustomerInterface::class, $quoteCustomer, $customer);
10611061
$this->setCustomer($quoteCustomer);
10621062
return $this;
10631063
}

dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ protected function _getCustomerDataArray()
393393
\Magento\Customer\Model\Data\Customer::DOB => '2014-02-03 00:00:00',
394394
\Magento\Customer\Model\Data\Customer::EMAIL => 'qa@example.com',
395395
\Magento\Customer\Model\Data\Customer::FIRSTNAME => 'Joe',
396-
\Magento\Customer\Model\Data\Customer::GENDER => 'Male',
396+
\Magento\Customer\Model\Data\Customer::GENDER => 0,
397397
\Magento\Customer\Model\Data\Customer::GROUP_ID =>
398398
\Magento\Customer\Model\GroupManagement::NOT_LOGGED_IN_ID,
399399
\Magento\Customer\Model\Data\Customer::ID => 1,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Reflection\Test\Unit\Fixture;
7+
8+
class TSample implements TSampleInterface
9+
{
10+
/**
11+
* @inheritdoc
12+
*/
13+
public function getPropertyName()
14+
{
15+
return '';
16+
}
17+
18+
/**
19+
* @inheritdoc
20+
*/
21+
public function getName()
22+
{
23+
return '';
24+
}
25+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Framework\Reflection\Test\Unit\Fixture;
7+
8+
interface TSampleInterface
9+
{
10+
/**
11+
* Returns property name for a sample.
12+
*
13+
* @return string
14+
*/
15+
public function getPropertyName();
16+
17+
/**
18+
* Doc block without return tag.
19+
*/
20+
public function getName();
21+
}

lib/internal/Magento/Framework/Reflection/Test/Unit/TypeProcessorTest.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
// @codingStandardsIgnoreStart
77
namespace Magento\Framework\Reflection\Test\Unit;
88

9-
use Zend\Code\Reflection\ClassReflection;
109
use Magento\Framework\Exception\SerializationException;
10+
use Magento\Framework\Reflection\Test\Unit\Fixture\TSample;
11+
use Zend\Code\Reflection\ClassReflection;
1112

1213
/**
1314
* Type processor Test
@@ -260,4 +261,35 @@ public function testGetOperationName()
260261
{
261262
$this->assertEquals("resNameMethodName", $this->_typeProcessor->getOperationName("resName", "methodName"));
262263
}
264+
265+
/**
266+
* Checks a case when method has only `@inheritdoc` annotation.
267+
*/
268+
public function testGetReturnTypeWithInheritDocBlock()
269+
{
270+
$expected = [
271+
'type' => 'string',
272+
'isRequired' => true,
273+
'description' => null,
274+
'parameterCount' => 0
275+
];
276+
277+
$classReflection = new ClassReflection(TSample::class);
278+
$methodReflection = $classReflection->getMethod('getPropertyName');
279+
280+
self::assertEquals($expected, $this->_typeProcessor->getGetterReturnType($methodReflection));
281+
}
282+
283+
/**
284+
* Checks a case when method and parent interface don't have `@return` annotation.
285+
*
286+
* @expectedException \InvalidArgumentException
287+
* @expectedExceptionMessage Getter return type must be specified using @return annotation. See Magento\Framework\Reflection\Test\Unit\Fixture\TSample::getName()
288+
*/
289+
public function testGetReturnTypeWithoutReturnTag()
290+
{
291+
$classReflection = new ClassReflection(TSample::class);
292+
$methodReflection = $classReflection->getMethod('getName');
293+
$this->_typeProcessor->getGetterReturnType($methodReflection);
294+
}
263295
}

lib/internal/Magento/Framework/Reflection/TypeProcessor.php

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Magento\Framework\Exception\SerializationException;
99
use Magento\Framework\Phrase;
1010
use Zend\Code\Reflection\ClassReflection;
11+
use Zend\Code\Reflection\DocBlock\Tag\ReturnTag;
1112
use Zend\Code\Reflection\DocBlockReflection;
1213
use Zend\Code\Reflection\MethodReflection;
1314
use Zend\Code\Reflection\ParameterReflection;
@@ -16,6 +17,7 @@
1617
* Type processor of config reader properties
1718
*
1819
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
20+
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity) this suppress MUST be removed after removing deprecated methods.
1921
*/
2022
class TypeProcessor
2123
{
@@ -275,22 +277,7 @@ protected function dataObjectGetterDescriptionToFieldDescription($shortDescripti
275277
*/
276278
public function getGetterReturnType($methodReflection)
277279
{
278-
$methodDocBlock = $methodReflection->getDocBlock();
279-
if (!$methodDocBlock) {
280-
throw new \InvalidArgumentException(
281-
"Each getter must have description with @return annotation. "
282-
. "See {$methodReflection->getDeclaringClass()->getName()}::{$methodReflection->getName()}()"
283-
);
284-
}
285-
$returnAnnotations = $methodDocBlock->getTags('return');
286-
if (empty($returnAnnotations)) {
287-
throw new \InvalidArgumentException(
288-
"Getter return type must be specified using @return annotation. "
289-
. "See {$methodReflection->getDeclaringClass()->getName()}::{$methodReflection->getName()}()"
290-
);
291-
}
292-
/** @var \Zend\Code\Reflection\DocBlock\Tag\ReturnTag $returnAnnotation */
293-
$returnAnnotation = current($returnAnnotations);
280+
$returnAnnotation = $this->getMethodReturnAnnotation($methodReflection);
294281
$types = $returnAnnotation->getTypes();
295282
$returnType = current($types);
296283
$nullable = in_array('null', $types);
@@ -362,7 +349,7 @@ public function isTypeSimple($type)
362349
self::NORMALIZED_INT_TYPE,
363350
self::NORMALIZED_FLOAT_TYPE,
364351
self::NORMALIZED_DOUBLE_TYPE,
365-
self::NORMALIZED_BOOLEAN_TYPE
352+
self::NORMALIZED_BOOLEAN_TYPE,
366353
]
367354
);
368355
}
@@ -708,4 +695,56 @@ private function getNormalizedType($type)
708695
}
709696
return $type;
710697
}
698+
699+
/**
700+
* Parses `return` annotation from reflection method.
701+
*
702+
* @param MethodReflection $methodReflection
703+
* @return ReturnTag
704+
* @throws \InvalidArgumentException if doc block is empty or `@return` annotation doesn't exist
705+
*/
706+
private function getMethodReturnAnnotation(MethodReflection $methodReflection)
707+
{
708+
$methodName = $methodReflection->getName();
709+
$returnAnnotations = $this->getReturnFromDocBlock($methodReflection);
710+
if (empty($returnAnnotations)) {
711+
// method can inherit doc block from implemented interface, like for interceptors
712+
$implemented = $methodReflection->getDeclaringClass()->getInterfaces();
713+
/** @var ClassReflection $parentClassReflection */
714+
foreach ($implemented as $parentClassReflection) {
715+
if ($parentClassReflection->hasMethod($methodName)) {
716+
$returnAnnotations = $this->getReturnFromDocBlock(
717+
$parentClassReflection->getMethod($methodName)
718+
);
719+
break;
720+
}
721+
}
722+
// throw an exception if even implemented interface doesn't have return annotations
723+
if (empty($returnAnnotations)) {
724+
throw new \InvalidArgumentException(
725+
"Getter return type must be specified using @return annotation. "
726+
. "See {$methodReflection->getDeclaringClass()->getName()}::{$methodName}()"
727+
);
728+
}
729+
}
730+
return $returnAnnotations;
731+
}
732+
733+
/**
734+
* Parses `return` annotation from doc block.
735+
*
736+
* @param MethodReflection $methodReflection
737+
* @return ReturnTag
738+
*/
739+
private function getReturnFromDocBlock(MethodReflection $methodReflection)
740+
{
741+
$methodDocBlock = $methodReflection->getDocBlock();
742+
if (!$methodDocBlock) {
743+
throw new \InvalidArgumentException(
744+
"Each getter must have a doc block. "
745+
. "See {$methodReflection->getDeclaringClass()->getName()}::{$methodReflection->getName()}()"
746+
);
747+
}
748+
return current($methodDocBlock->getTags('return'));
749+
}
711750
}

0 commit comments

Comments
 (0)