11
11
use Magento \Framework \ObjectManagerInterface ;
12
12
13
13
/**
14
- * Collects shared objects from ObjectManager and clones properties for later comparison
14
+ * Collects shared objects from ObjectManager and copies properties for later comparison
15
15
*/
16
16
class Collector
17
17
{
18
- /**
19
- * @var ObjectManagerInterface
20
- */
21
- private ObjectManagerInterface $ objectManager ;
18
+ private array $ skipListFromConstructed ;
19
+ private array $ skipListBetweenRequests ;
22
20
23
- /**
24
- * @param ObjectManagerInterface $objectManager
25
- */
26
- public function __construct (ObjectManagerInterface $ objectManager )
27
- {
28
- $ this ->objectManager = $ objectManager ;
21
+ public function __construct (
22
+ private ObjectManagerInterface $ objectManager ,
23
+ SkipListAndFilterList $ skipListAndFilterList
24
+ ) {
25
+ $ this ->skipListFromConstructed =
26
+ $ skipListAndFilterList ->getSkipList ('' , CompareType::CompareConstructedAgainstCurrent);
27
+ $ this ->skipListBetweenRequests = $ skipListAndFilterList ->getSkipList ('' , CompareType::CompareBetweenRequests);
29
28
}
30
29
31
30
/**
32
- * Recursively clone objects in array.
33
- *
34
- * TODO: avoid infinite recursion when $array is cyclic.
31
+ * Recursively copy objects in array.
35
32
*
36
33
* @param array $array
37
- * @param bool $callResetState
34
+ * @param CompareType $compareType
35
+ * @param int $recursionLevel
36
+ * @param int $arrayRecursionLevel
38
37
* @return array
39
38
*/
40
- private function cloneArray (array $ array , bool $ callResetState ) : array
39
+ private function copyArray (
40
+ array $ array ,
41
+ CompareType $ compareType ,
42
+ int $ recursionLevel ,
43
+ int $ arrayRecursionLevel = 100
44
+ ) : array
41
45
{
42
46
return array_map (
43
- function ($ element ) use ($ callResetState ) {
47
+ function ($ element ) use (
48
+ $ compareType ,
49
+ $ recursionLevel ,
50
+ $ arrayRecursionLevel ,
51
+ ) {
44
52
if (is_object ($ element )) {
45
- if ($ callResetState && $ element instanceof ResetAfterRequestInterface) {
46
- $ element ->_resetState ();
47
- }
48
- return clone $ element ;
53
+ return $ this ->getPropertiesFromObject (
54
+ $ element ,
55
+ $ compareType ,
56
+ $ recursionLevel - 1 ,
57
+ );
49
58
}
50
59
if (is_array ($ element )) {
51
- return $ this ->cloneArray ($ element , $ callResetState );
60
+ if ($ arrayRecursionLevel ) {
61
+ return $ this ->copyArray (
62
+ $ element ,
63
+ $ compareType ,
64
+ $ recursionLevel ,
65
+ $ arrayRecursionLevel - 1 ,
66
+ );
67
+ } else {
68
+ return '(end of array recursion level) ' ;
69
+ }
52
70
}
53
71
return $ element ;
54
72
},
@@ -59,42 +77,41 @@ function ($element) use ($callResetState) {
59
77
/**
60
78
* Gets shared objects from ObjectManager using reflection and clones properties that are objects
61
79
*
62
- * @return array
63
- * @throws \Exception
80
+ * @param ShouldResetState $shouldResetState
81
+ * @return CollectedObject[]
64
82
*/
65
- public function getSharedObjects (): array
83
+ public function getSharedObjects (ShouldResetState $ shouldResetState ): array
66
84
{
67
85
$ sharedObjects = [];
68
86
$ obj = new \ReflectionObject ($ this ->objectManager );
69
87
if (!$ obj ->hasProperty ('_sharedInstances ' )) {
70
88
throw new \Exception ('Cannot get shared objects from ' . get_class ($ this ->objectManager ));
71
89
}
72
- do {
73
- $ property = $ obj ->getProperty ('_sharedInstances ' );
74
- $ property ->setAccessible (true );
75
- $ didClone = false ;
76
- foreach ($ property ->getValue ($ this ->objectManager ) as $ serviceName => $ object ) {
77
- if (array_key_exists ($ serviceName , $ sharedObjects )) {
78
- continue ;
79
- }
80
- if ($ object instanceof \Magento \Framework \ObjectManagerInterface) {
81
- continue ;
82
- }
83
- if ($ object instanceof ResetAfterRequestInterface) {
84
- $ object ->_resetState ();
85
- }
86
- $ properties = $ this ->getPropertiesFromObject ($ object , true , $ didClone );
87
- $ sharedObjects [$ serviceName ] = [$ object , $ properties ];
90
+ $ property = $ obj ->getProperty ('_sharedInstances ' );
91
+ $ property ->setAccessible (true );
92
+ foreach ($ property ->getValue ($ this ->objectManager ) as $ serviceName => $ object ) {
93
+ if (array_key_exists ($ serviceName , $ sharedObjects )) {
94
+ continue ;
95
+ }
96
+ if (ShouldResetState::DoResetState == $ shouldResetState &&
97
+ ($ object instanceof ResetAfterRequestInterface)) {
98
+ $ object ->_resetState ();
88
99
}
89
- // Note: We have to check again because sometimes cloning objects can indirectly cause adding to Object Manager
90
- } while ($ didClone );
100
+ if ($ object instanceof \Magento \Framework \ObjectManagerInterface) {
101
+ continue ;
102
+ }
103
+ $ sharedObjects [$ serviceName ] =
104
+ $ this ->getPropertiesFromObject ($ object , CompareType::CompareBetweenRequests);
105
+ }
91
106
return $ sharedObjects ;
92
107
}
93
108
94
109
/**
95
110
* Gets all the objects' properties as they were originally constructed, and current, as well as object itself
96
111
*
97
- * @return array
112
+ * This also calls _resetState on any ResetAfterRequestInterface
113
+ *
114
+ * @return CollectedObjectConstructedAndCurrent[]
98
115
*/
99
116
public function getPropertiesConstructedAndCurrent (): array
100
117
{
@@ -115,42 +132,63 @@ public function getPropertiesConstructedAndCurrent(): array
115
132
$ collectedCount = gc_collect_cycles ();
116
133
$ objects = [];
117
134
foreach ($ objectManager ->getWeakMap () as $ object => $ propertiesBefore ) {
118
- $ objects [] = [
119
- ' object ' => $ object ,
120
- ' constructedProperties ' => $ propertiesBefore ,
121
- ' currentProperties ' => $ this ->getPropertiesFromObject ($ object ),
122
- ] ;
135
+ $ objects [] = new CollectedObjectConstructedAndCurrent (
136
+ $ object ,
137
+ $ propertiesBefore ,
138
+ $ this ->getPropertiesFromObject ($ object, CompareType::CompareConstructedAgainstCurrent ),
139
+ ) ;
123
140
}
124
141
return $ objects ;
125
142
}
126
143
127
- public function getPropertiesFromObject (object $ object , $ doClone = false , &$ didClone = null ): array
128
- {
129
- $ callResetState = true ;
144
+ /**
145
+ * Gets properties from object and returns CollectedObject
146
+ *
147
+ * @param object $object
148
+ * @param CompareType $compareType
149
+ * @param int $recursionLevel
150
+ * @return CollectedObject
151
+ */
152
+ public function getPropertiesFromObject (
153
+ object $ object ,
154
+ CompareType $ compareType ,
155
+ int $ recursionLevel = 1 ,
156
+ ): CollectedObject {
157
+ if ($ recursionLevel < 0 ) {
158
+ return CollectedObject::getRecursionEndObject ();
159
+ }
160
+ $ className = get_class ($ object );
161
+ $ skipList = $ compareType == CompareType::CompareBetweenRequests ?
162
+ $ this ->skipListBetweenRequests : $ this ->skipListFromConstructed ;
163
+ if (array_key_exists ($ className , $ skipList )) {
164
+ return CollectedObject::getSkippedObject ();
165
+ }
130
166
$ objReflection = new \ReflectionObject ($ object );
131
167
$ properties = [];
132
168
foreach ($ objReflection ->getProperties () as $ property ) {
133
- $ propName = $ property ->getName ();
169
+ $ propertyName = $ property ->getName ();
134
170
$ property ->setAccessible (true );
135
171
$ value = $ property ->getValue ($ object );
136
- if (!$ doClone ) {
137
- $ properties [$ propName ] = $ value ;
138
- continue ;
139
- }
140
172
if (is_object ($ value )) {
141
- $ didClone = true ;
142
- if ($ callResetState && $ value instanceof ResetAfterRequestInterface) {
143
- // Calling _resetState helps us avoid adding skip/filter for these classes.
144
- $ value ->_resetState ();
145
- }
146
- $ properties [$ propName ] = clone $ value ;
173
+ $ properties [$ propertyName ] = $ this ->getPropertiesFromObject (
174
+ $ value ,
175
+ $ compareType ,
176
+ $ recursionLevel - 1 ,
177
+ );
147
178
} elseif (is_array ($ value )) {
148
- $ didClone = true ;
149
- $ properties [$ propName ] = $ this ->cloneArray ($ value , $ callResetState );
179
+ $ properties [$ propertyName ] = $ this ->copyArray (
180
+ $ value ,
181
+ $ compareType ,
182
+ $ recursionLevel ,
183
+ );
150
184
} else {
151
- $ properties [$ propName ] = $ value ;
185
+ $ properties [$ propertyName ] = $ value ;
152
186
}
153
187
}
154
- return $ properties ;
188
+ return new CollectedObject (
189
+ $ className ,
190
+ $ properties ,
191
+ spl_object_id ($ object ),
192
+ );
155
193
}
156
194
}
0 commit comments