|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Kaliop\eZWorkflowEngineBundle\Core; |
|
4
|
|
|
|
|
5
|
|
|
use Kaliop\eZMigrationBundle\API\Collection\MigrationDefinitionCollection; |
|
6
|
|
|
use Kaliop\eZMigrationBundle\API\DefinitionParserInterface; |
|
7
|
|
|
use Kaliop\eZMigrationBundle\API\ExecutorInterface; |
|
8
|
|
|
use Kaliop\eZMigrationBundle\API\Value\MigrationDefinition; |
|
9
|
|
|
use Kaliop\eZMigrationBundle\Core\MigrationService; |
|
10
|
|
|
use Kaliop\eZWorkflowEngineBundle\API\Value\WorkflowDefinition; |
|
11
|
|
|
use Psr\Log\LoggerInterface; |
|
12
|
|
|
use Symfony\Component\Config\ConfigCache; |
|
13
|
|
|
use Symfony\Component\Config\Resource\FileResource; |
|
14
|
|
|
|
|
15
|
|
|
/** |
|
16
|
|
|
* @todo add phpdoc to help IDEs |
|
17
|
|
|
*/ |
|
18
|
|
|
class WorkflowServiceFacade |
|
19
|
|
|
{ |
|
20
|
|
|
protected $innerService; |
|
21
|
|
|
protected $cacheDir; |
|
22
|
|
|
protected $debugMode; |
|
23
|
|
|
protected $logger; |
|
24
|
|
|
protected $recursionLimit; |
|
25
|
|
|
protected static $workflowExecuting = 0; |
|
26
|
|
|
|
|
27
|
|
|
public function __construct(MigrationService $innerService, $recursionLimit, $cacheDir, $debugMode = false, |
|
28
|
|
|
LoggerInterface $logger = null) |
|
29
|
|
|
{ |
|
30
|
|
|
$this->innerService = $innerService; |
|
31
|
|
|
$this->recursionLimit = $recursionLimit; |
|
32
|
|
|
$this->cacheDir = $cacheDir; |
|
33
|
|
|
$this->debugMode = $debugMode; |
|
34
|
|
|
$this->logger = $logger; |
|
35
|
|
|
} |
|
36
|
|
|
|
|
37
|
|
|
/** |
|
38
|
|
|
* @param string $signalName must use the same format as we extract from signal class names |
|
39
|
|
|
* @param array $signalParameters must follow what is found in eZ5 signals |
|
40
|
|
|
* @throws \Exception |
|
41
|
|
|
*/ |
|
42
|
|
|
public function triggerWorkflow($signalName, array $signalParameters) |
|
43
|
|
|
{ |
|
44
|
|
|
$workflowDefinitions = $this->getValidWorkflowsDefinitionsForSignal($signalName); |
|
45
|
|
|
|
|
46
|
|
|
if ($this->logger) $this->logger->debug("Found " . count($workflowDefinitions) . " workflow definitions for signal '$signalName'"); |
|
47
|
|
|
|
|
48
|
|
|
if (count($workflowDefinitions)) { |
|
49
|
|
|
|
|
50
|
|
|
$workflowParameters = array(); |
|
51
|
|
|
foreach($signalParameters as $parameter => $value) { |
|
52
|
|
|
$convertedParameter = $this->convertSignalMember($signalName, $parameter); |
|
53
|
|
|
$workflowParameters['signal:' . $convertedParameter] = $value; |
|
54
|
|
|
} |
|
55
|
|
|
|
|
56
|
|
|
/** @var WorkflowDefinition $workflowDefinition */ |
|
57
|
|
|
foreach ($workflowDefinitions as $workflowDefinition) { |
|
58
|
|
|
|
|
59
|
|
|
if (self::$workflowExecuting > 0 && $workflowDefinition->avoidRecursion) { |
|
60
|
|
|
if ($this->logger) $this->logger->debug("Skipping workflow '{$workflowDefinition->name}' to avoid recursion (workflow already executing)"); |
|
61
|
|
|
return; |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
if (self::$workflowExecuting >= $this->recursionLimit) { |
|
65
|
|
|
throw new \Exception("Workflow execution halted as we reached {$this->recursionLimit} nested workflows"); |
|
66
|
|
|
} |
|
67
|
|
|
|
|
68
|
|
|
$wfd = new WorkflowDefinition( |
|
69
|
|
|
$workflowDefinition->name, |
|
70
|
|
|
$workflowDefinition->path, |
|
71
|
|
|
$workflowDefinition->rawDefinition, |
|
72
|
|
|
$workflowDefinition->status, |
|
73
|
|
|
$workflowDefinition->steps->getArrayCopy(), |
|
74
|
|
|
null, |
|
75
|
|
|
$signalName, |
|
76
|
|
|
$workflowDefinition->runAs, |
|
77
|
|
|
$workflowDefinition->useTransaction, |
|
78
|
|
|
$workflowDefinition->avoidRecursion |
|
79
|
|
|
); |
|
80
|
|
|
|
|
81
|
|
|
self::$workflowExecuting += 1; |
|
82
|
|
|
try { |
|
83
|
|
|
|
|
84
|
|
|
if ($this->logger) $this->logger->debug("Executing workflow '{$workflowDefinition->name}' with parameters: " . preg_replace("/\n+/s", ' ', preg_replace('/^(Array| +|\(|\))/m', '', print_r($workflowParameters, true)))); |
|
85
|
|
|
|
|
86
|
|
|
/// @todo allow setting of default lang, user and userGroup content types ? |
|
87
|
|
|
$this->innerService->executeMigration($wfd, array( |
|
88
|
|
|
'useTransaction' => $workflowDefinition->useTransaction, |
|
89
|
|
|
'adminUserLogin' => $workflowDefinition->runAs, |
|
90
|
|
|
'workflow' => $workflowParameters, |
|
91
|
|
|
)); |
|
92
|
|
|
|
|
93
|
|
|
self::$workflowExecuting -= 1; |
|
94
|
|
|
} catch (\Exception $e) { |
|
95
|
|
|
self::$workflowExecuting -= 1; |
|
96
|
|
|
throw $e; |
|
97
|
|
|
} |
|
98
|
|
|
} |
|
99
|
|
|
} |
|
100
|
|
|
} |
|
101
|
|
|
|
|
102
|
|
|
/** |
|
103
|
|
|
* @param string $signalName |
|
104
|
|
|
* @param string $parameter |
|
105
|
|
|
* @return string |
|
106
|
|
|
*/ |
|
107
|
|
|
protected function convertSignalMember($signalName, $parameter) |
|
108
|
|
|
{ |
|
109
|
|
|
// CamelCase to snake_case using negative look-behind in regexp |
|
110
|
|
|
return strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $parameter)); |
|
111
|
|
|
} |
|
112
|
|
|
|
|
113
|
|
|
/** |
|
114
|
|
|
* Unlike its parent's similar function, this one only deals with *parsed* definitions. |
|
115
|
|
|
* NB: this function, unlike getValidWorkflowsDefinitionsForSignal, does not cache its results, which might lead to |
|
116
|
|
|
* some hard-to troubleshoot weirdness... |
|
117
|
|
|
* @param string[] $paths |
|
118
|
|
|
* @return MigrationDefinitionCollection |
|
119
|
|
|
*/ |
|
120
|
|
|
public function getWorkflowsDefinitions($paths = array()) |
|
121
|
|
|
{ |
|
122
|
|
|
$defs = array(); |
|
123
|
|
|
|
|
124
|
|
|
foreach($this->innerService->getMigrationsDefinitions() as $key => $definition) { |
|
125
|
|
|
if ($definition->status == MigrationDefinition::STATUS_TO_PARSE) { |
|
126
|
|
|
$definition = $this->innerService->parseMigrationDefinition($definition); |
|
127
|
|
|
} |
|
128
|
|
|
$defs[$key] = $definition; |
|
129
|
|
|
} |
|
130
|
|
|
|
|
131
|
|
|
return new MigrationDefinitionCollection($defs); |
|
132
|
|
|
} |
|
133
|
|
|
|
|
134
|
|
|
/** |
|
135
|
|
|
* Returns VALID definitions for a given signal. |
|
136
|
|
|
* Uses the Sf cache to speed up the process (recipe taken from http://symfony.com/doc/2.7/components/config/caching.html) |
|
137
|
|
|
* @param $signalName |
|
138
|
|
|
* @param string[] $paths |
|
139
|
|
|
* @return MigrationDefinitionCollection |
|
140
|
|
|
*/ |
|
141
|
|
|
public function getValidWorkflowsDefinitionsForSignal($signalName, $paths = array()) |
|
142
|
|
|
{ |
|
143
|
|
|
$cacheFile = $this->cacheDir . '/' . md5($signalName) . '/' . md5(serialize($paths)) . '.php'; |
|
144
|
|
|
|
|
145
|
|
|
$cache = new ConfigCache($cacheFile, $this->debugMode); |
|
146
|
|
|
if ($cache->isFresh()) { |
|
147
|
|
|
return require $cacheFile; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
$defs = array(); |
|
151
|
|
|
$resources = array(); |
|
152
|
|
|
|
|
153
|
|
|
foreach($this->getWorkflowsDefinitions($paths) as $key => $definition) { |
|
154
|
|
|
/// @todo add safety check that we got back in fact a WorkflowDefinition |
|
155
|
|
|
if ($definition->signalName === $signalName && $definition->status == MigrationDefinition::STATUS_PARSED) { |
|
156
|
|
|
$defs[$key] = $definition; |
|
157
|
|
|
$resources[] = new FileResource($definition->path); |
|
158
|
|
|
} |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
$collection = new MigrationDefinitionCollection($defs); |
|
162
|
|
|
|
|
163
|
|
|
$code = '<?php return '.var_export($collection, true).';'; |
|
164
|
|
|
$cache->write($code, $resources); |
|
165
|
|
|
|
|
166
|
|
|
return $collection; |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
public function __call($name, array $arguments) |
|
170
|
|
|
{ |
|
171
|
|
|
$name = str_replace('Workflow', 'Migration', $name); |
|
172
|
|
|
return call_user_func_array(array($this->innerService, $name), $arguments); |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
// try to keep all Sf versions happy: some do apparently complain if this method is only available via __call |
|
176
|
|
|
public function addDefinitionParser(DefinitionParserInterface $executor) |
|
177
|
|
|
{ |
|
178
|
|
|
return $this->innerService->addDefinitionParser($executor); |
|
|
|
|
|
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
// same |
|
182
|
|
|
public function addExecutor(ExecutorInterface $executor) |
|
183
|
|
|
{ |
|
184
|
|
|
return $this->innerService->addExecutor($executor); |
|
|
|
|
|
|
185
|
|
|
} |
|
186
|
|
|
} |
|
187
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.