11
11
12
12
namespace Symfony \Component \Workflow \DataCollector ;
13
13
14
+ use Symfony \Component \ErrorHandler \ErrorRenderer \FileLinkFormatter ;
15
+ use Symfony \Component \EventDispatcher \EventDispatcherInterface ;
14
16
use Symfony \Component \HttpFoundation \Request ;
15
17
use Symfony \Component \HttpFoundation \Response ;
16
18
use Symfony \Component \HttpKernel \DataCollector \DataCollector ;
19
21
use Symfony \Component \VarDumper \Cloner \Stub ;
20
22
use Symfony \Component \Workflow \Debug \TraceableWorkflow ;
21
23
use Symfony \Component \Workflow \Dumper \MermaidDumper ;
24
+ use Symfony \Component \Workflow \EventListener \GuardExpression ;
25
+ use Symfony \Component \Workflow \EventListener \GuardListener ;
22
26
use Symfony \Component \Workflow \Marking ;
27
+ use Symfony \Component \Workflow \Transition ;
23
28
use Symfony \Component \Workflow \TransitionBlocker ;
29
+ use Symfony \Component \Workflow \WorkflowInterface ;
24
30
25
31
/**
26
32
* @author Grégoire Pineau <lyrixx@lyrixx.info>
@@ -29,6 +35,8 @@ final class WorkflowDataCollector extends DataCollector implements LateDataColle
29
35
{
30
36
public function __construct (
31
37
private readonly iterable $ workflows ,
38
+ private readonly EventDispatcherInterface $ eventDispatcher ,
39
+ private readonly FileLinkFormatter $ fileLinkFormatter ,
32
40
) {
33
41
}
34
42
@@ -50,6 +58,7 @@ public function lateCollect(): void
50
58
$ this ->data ['workflows ' ][$ workflow ->getName ()] = [
51
59
'dump ' => $ dumper ->dump ($ workflow ->getDefinition ()),
52
60
'calls ' => $ calls ,
61
+ 'listeners ' => $ this ->getEventListeners ($ workflow ),
53
62
];
54
63
}
55
64
}
@@ -102,4 +111,120 @@ protected function getCasters(): array
102
111
103
112
return $ casters ;
104
113
}
114
+
115
+ public function hash (string $ string ): string
116
+ {
117
+ return hash ('xxh128 ' , $ string );
118
+ }
119
+
120
+ private function getEventListeners (WorkflowInterface $ workflow ): array
121
+ {
122
+ $ listeners = [];
123
+ $ placeId = 0 ;
124
+ foreach ($ workflow ->getDefinition ()->getPlaces () as $ place ) {
125
+ $ eventNames = [];
126
+ $ subEventNames = [
127
+ 'leave ' ,
128
+ 'enter ' ,
129
+ 'entered ' ,
130
+ ];
131
+ foreach ($ subEventNames as $ subEventName ) {
132
+ $ eventNames [] = sprintf ('workflow.%s ' , $ subEventName );
133
+ $ eventNames [] = sprintf ('workflow.%s.%s ' , $ workflow ->getName (), $ subEventName );
134
+ $ eventNames [] = sprintf ('workflow.%s.%s.%s ' , $ workflow ->getName (), $ subEventName , $ place );
135
+ }
136
+ foreach ($ eventNames as $ eventName ) {
137
+ foreach ($ this ->eventDispatcher ->getListeners ($ eventName ) as $ listener ) {
138
+ $ listeners ["place {$ placeId }" ][$ eventName ][] = $ this ->summarizeListener ($ listener );
139
+ }
140
+ }
141
+
142
+ ++$ placeId ;
143
+ }
144
+
145
+ foreach ($ workflow ->getDefinition ()->getTransitions () as $ transitionId => $ transition ) {
146
+ $ eventNames = [];
147
+ $ subEventNames = [
148
+ 'guard ' ,
149
+ 'transition ' ,
150
+ 'completed ' ,
151
+ 'announce ' ,
152
+ ];
153
+ foreach ($ subEventNames as $ subEventName ) {
154
+ $ eventNames [] = sprintf ('workflow.%s ' , $ subEventName );
155
+ $ eventNames [] = sprintf ('workflow.%s.%s ' , $ workflow ->getName (), $ subEventName );
156
+ $ eventNames [] = sprintf ('workflow.%s.%s.%s ' , $ workflow ->getName (), $ subEventName , $ transition ->getName ());
157
+ }
158
+ foreach ($ eventNames as $ eventName ) {
159
+ foreach ($ this ->eventDispatcher ->getListeners ($ eventName ) as $ listener ) {
160
+ $ listeners ["transition {$ transitionId }" ][$ eventName ][] = $ this ->summarizeListener ($ listener , $ eventName , $ transition );
161
+ }
162
+ }
163
+ }
164
+
165
+ return $ listeners ;
166
+ }
167
+
168
+ private function summarizeListener (callable $ callable , string $ eventName = null , Transition $ transition = null ): array
169
+ {
170
+ $ extra = [];
171
+
172
+ if ($ callable instanceof \Closure) {
173
+ $ r = new \ReflectionFunction ($ callable );
174
+ if (str_contains ($ r ->name , '{closure} ' )) {
175
+ $ title = (string ) $ r ;
176
+ } elseif ($ class = \PHP_VERSION_ID >= 80111 ? $ r ->getClosureCalledClass () : $ r ->getClosureScopeClass ()) {
177
+ $ title = $ class ->name .':: ' .$ r ->name .'() ' ;
178
+ } else {
179
+ $ title = $ r ->name ;
180
+ }
181
+ } elseif (\is_string ($ callable )) {
182
+ $ title = $ callable .'() ' ;
183
+ $ r = new \ReflectionFunction ($ callable );
184
+ } elseif (\is_object ($ callable ) && method_exists ($ callable , '__invoke ' )) {
185
+ $ r = new \ReflectionMethod ($ callable , '__invoke ' );
186
+ $ title = $ callable ::class.'::__invoke() ' ;
187
+ } elseif (\is_array ($ callable )) {
188
+ if ($ callable [0 ] instanceof GuardListener) {
189
+ if (null === $ eventName || null === $ transition ) {
190
+ throw new \LogicException ('Missing event name or transition. ' );
191
+ }
192
+ $ extra ['guardExpressions ' ] = $ this ->extractGuardExpressions ($ callable [0 ], $ eventName , $ transition );
193
+ }
194
+ $ r = new \ReflectionMethod ($ callable [0 ], $ callable [1 ]);
195
+ $ title = (\is_string ($ callable [0 ]) ? $ callable [0 ] : \get_class ($ callable [0 ])).':: ' .$ callable [1 ].'() ' ;
196
+ } else {
197
+ throw new \RuntimeException ('Unknown callable type. ' );
198
+ }
199
+
200
+ $ file = null ;
201
+ if ($ r ->isUserDefined ()) {
202
+ $ file = $ this ->fileLinkFormatter ->format ($ r ->getFileName (), $ r ->getStartLine ());
203
+ }
204
+
205
+ return [
206
+ 'title ' => $ title ,
207
+ 'file ' => $ file ,
208
+ ...$ extra ,
209
+ ];
210
+ }
211
+
212
+ private function extractGuardExpressions (GuardListener $ listener , string $ eventName , Transition $ transition ): array
213
+ {
214
+ $ configuration = (new \ReflectionProperty (GuardListener::class, 'configuration ' ))->getValue ($ listener );
215
+
216
+ $ expressions = [];
217
+ foreach ($ configuration [$ eventName ] as $ guard ) {
218
+ if ($ guard instanceof GuardExpression) {
219
+ if ($ guard ->getTransition () !== $ transition ) {
220
+ continue ;
221
+ }
222
+ $ expressions [] = $ guard ->getExpression ();
223
+ } else {
224
+ $ expressions [] = $ guard ;
225
+ }
226
+ }
227
+
228
+ return $ expressions ;
229
+ }
105
230
}
0 commit comments