GlobalTradingTechnologies /
workflow-extensions-bundle
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * This file is part of the Global Trading Technologies Ltd workflow-extension-bundle package. |
||
| 4 | * |
||
| 5 | * For the full copyright and license information, please view the LICENSE |
||
| 6 | * file that was distributed with this source code. |
||
| 7 | * |
||
| 8 | * (c) fduch <[email protected]> |
||
| 9 | * @date 17.07.15 |
||
| 10 | */ |
||
| 11 | |||
| 12 | namespace Gtt\Bundle\WorkflowExtensionsBundle\DependencyInjection; |
||
| 13 | |||
| 14 | use Gtt\Bundle\WorkflowExtensionsBundle\Action\Reference\ActionReferenceInterface; |
||
| 15 | use Gtt\Bundle\WorkflowExtensionsBundle\DependencyInjection\Enum\ActionArgumentTypes; |
||
| 16 | use Gtt\Bundle\WorkflowExtensionsBundle\Utils\ArrayUtils; |
||
| 17 | use ReflectionClass; |
||
| 18 | use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; |
||
| 19 | use Symfony\Component\Config\Definition\Builder\NodeDefinition; |
||
| 20 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; |
||
| 21 | use Symfony\Component\Config\Definition\ConfigurationInterface; |
||
| 22 | use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; |
||
| 23 | use Symfony\Component\Config\Definition\Processor; |
||
| 24 | use DateInterval; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * Configuration class for DI |
||
| 28 | */ |
||
| 29 | class Configuration implements ConfigurationInterface |
||
| 30 | { |
||
| 31 | /** |
||
| 32 | * Generates the configuration tree builder. |
||
| 33 | * |
||
| 34 | * @return TreeBuilder The tree builder |
||
| 35 | */ |
||
| 36 | public function getConfigTreeBuilder() |
||
| 37 | { |
||
| 38 | $treeBuilder = new TreeBuilder(); |
||
| 39 | $rootNode = $treeBuilder->root('workflow_extensions'); |
||
| 40 | |||
| 41 | $rootNode |
||
| 42 | ->children() |
||
| 43 | ->append($this->addActionsSection()) |
||
| 44 | ->append($this->addWorkflowsSection()) |
||
| 45 | ->append($this->addSubjectManipulatorSection()) |
||
| 46 | ->append($this->addSchedulerSection()) |
||
| 47 | ->end() |
||
| 48 | ; |
||
| 49 | |||
| 50 | return $treeBuilder; |
||
| 51 | } |
||
| 52 | |||
| 53 | /** |
||
| 54 | * Defines config section defines list of application actions |
||
| 55 | * |
||
| 56 | * @return ArrayNodeDefinition |
||
| 57 | */ |
||
| 58 | private function addActionsSection() |
||
| 59 | { |
||
| 60 | $builder = new TreeBuilder(); |
||
| 61 | $node = $builder->root('actions'); |
||
| 62 | |||
| 63 | $node |
||
|
0 ignored issues
–
show
|
|||
| 64 | ->useAttributeAsKey('name') |
||
| 65 | ->cannotBeEmpty() |
||
| 66 | ->requiresAtLeastOneElement() |
||
| 67 | ->prototype('array') |
||
| 68 | ->children() |
||
| 69 | ->enumNode('type') |
||
| 70 | ->values([ActionReferenceInterface::TYPE_REGULAR, ActionReferenceInterface::TYPE_WORKFLOW]) |
||
| 71 | ->cannotBeEmpty() |
||
| 72 | ->defaultValue(ActionReferenceInterface::TYPE_REGULAR) |
||
| 73 | ->beforeNormalization() |
||
| 74 | ->ifString() |
||
| 75 | ->then(function($v) { |
||
| 76 | return constant(ActionReferenceInterface::class."::TYPE_".strtoupper($v)); |
||
| 77 | }) |
||
| 78 | ->end() |
||
| 79 | ->end() |
||
| 80 | ->scalarNode('service') |
||
| 81 | ->cannotBeEmpty() |
||
| 82 | ->info('Service id of the class contains action method') |
||
| 83 | ->end() |
||
| 84 | ->scalarNode('class') |
||
| 85 | ->cannotBeEmpty() |
||
| 86 | ->info('Class contains action method') |
||
| 87 | ->end() |
||
| 88 | ->scalarNode('method') |
||
| 89 | ->isRequired() |
||
| 90 | ->cannotBeEmpty() |
||
| 91 | ->info('Action method of the service') |
||
| 92 | ->end() |
||
| 93 | ->end() |
||
| 94 | ->end() |
||
| 95 | ->validate() |
||
| 96 | ->always() |
||
| 97 | ->then(function ($v) { |
||
| 98 | foreach ($v as $actionName => $actionConfig) { |
||
| 99 | if (!preg_match('/^[a-z0-9_]+$/i', $actionName)) { |
||
| 100 | throw new InvalidConfigurationException( |
||
| 101 | sprintf( |
||
| 102 | 'Action name must contain only alphanumeric and underscore symbols. Please rename action "%s"', |
||
| 103 | $actionName |
||
| 104 | ) |
||
| 105 | ); |
||
| 106 | } |
||
| 107 | if (!(array_key_exists('class', $actionConfig) xor array_key_exists('service', $actionConfig))) { |
||
| 108 | throw new InvalidConfigurationException( |
||
| 109 | sprintf( |
||
| 110 | 'Please change configuration for "%s" action. It is possible to set "class" or "service" option, but not the both', |
||
| 111 | $actionName |
||
| 112 | ) |
||
| 113 | ); |
||
| 114 | } |
||
| 115 | if (array_key_exists('class', $actionConfig)) { |
||
| 116 | if (!class_exists($actionConfig['class'])) { |
||
| 117 | throw new InvalidConfigurationException( |
||
| 118 | sprintf( |
||
| 119 | 'The class "%s" configured for "%s" action does not exist', |
||
| 120 | $actionConfig['class'], |
||
| 121 | $actionName |
||
| 122 | ) |
||
| 123 | ); |
||
| 124 | } |
||
| 125 | if (!method_exists($actionConfig['class'], $actionConfig['method'])) { |
||
| 126 | throw new InvalidConfigurationException( |
||
| 127 | sprintf( |
||
| 128 | 'The class "%s" configured for "%s" action does not have "%s" method', |
||
| 129 | $actionConfig['class'], |
||
| 130 | $actionName, |
||
| 131 | $actionConfig['method'] |
||
| 132 | ) |
||
| 133 | ); |
||
| 134 | } |
||
| 135 | } |
||
| 136 | |||
| 137 | return $v; |
||
| 138 | } |
||
| 139 | }) |
||
| 140 | ->end() |
||
| 141 | ; |
||
| 142 | |||
| 143 | return $node; |
||
| 144 | } |
||
| 145 | |||
| 146 | /** |
||
| 147 | * Defines workflows section |
||
| 148 | * |
||
| 149 | * @return ArrayNodeDefinition |
||
| 150 | */ |
||
| 151 | private function addWorkflowsSection() |
||
| 152 | { |
||
| 153 | $builder = new TreeBuilder(); |
||
| 154 | $node = $builder->root('workflows'); |
||
| 155 | |||
| 156 | $node |
||
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
Symfony\Component\Config...\Builder\NodeDefinition as the method children() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
Loading history...
|
|||
| 157 | ->fixXmlConfig('workflow') |
||
| 158 | ->isRequired() |
||
| 159 | ->cannotBeEmpty() |
||
| 160 | ->useAttributeAsKey('name') |
||
| 161 | ->prototype('array') |
||
| 162 | ->children() |
||
| 163 | ->append($this->addTriggersSection()) |
||
| 164 | ->append($this->addGuardSection()) |
||
| 165 | ->end() |
||
| 166 | ->end() |
||
| 167 | ; |
||
| 168 | |||
| 169 | return $node; |
||
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Defines triggers config section |
||
| 174 | * |
||
| 175 | * @return ArrayNodeDefinition |
||
| 176 | */ |
||
| 177 | private function addTriggersSection() |
||
| 178 | { |
||
| 179 | $builder = new TreeBuilder(); |
||
| 180 | $node = $builder->root('triggers'); |
||
| 181 | |||
| 182 | $node |
||
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
Symfony\Component\Config...\Builder\NodeDefinition as the method children() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
Loading history...
|
|||
| 183 | ->fixXmlConfig('trigger') |
||
| 184 | ->cannotBeEmpty() |
||
| 185 | ->children() |
||
| 186 | ->arrayNode('event') |
||
| 187 | ->useAttributeAsKey('name') |
||
| 188 | ->prototype('array') |
||
| 189 | ->children() |
||
| 190 | ->arrayNode('actions') |
||
| 191 | ->cannotBeEmpty() |
||
| 192 | ->defaultValue([]) |
||
| 193 | ->beforeNormalization() |
||
| 194 | ->ifString() |
||
| 195 | ->then(function ($v) { return array($v); }) |
||
| 196 | ->end() |
||
| 197 | ->useAttributeAsKey('name') |
||
| 198 | ->prototype('array') |
||
| 199 | ->prototype('array') |
||
| 200 | ->children() |
||
| 201 | ->append($this->buildActionArgumentsNode()) |
||
| 202 | ->end() |
||
| 203 | ->beforeNormalization() |
||
| 204 | ->ifString() |
||
| 205 | ->then(function ($v) { |
||
| 206 | return ['arguments' => [$v]]; |
||
| 207 | }) |
||
| 208 | ->end() |
||
| 209 | ->beforeNormalization() |
||
| 210 | ->ifTrue(function ($v) { |
||
| 211 | // place arguments under 'arguments' key in order to validate it in common way |
||
| 212 | return is_array($v) && !(count($v) == 1 && array_key_exists('arguments', $v)); |
||
| 213 | }) |
||
| 214 | ->then(function ($v) { |
||
| 215 | return ['arguments' => $v]; |
||
| 216 | }) |
||
| 217 | ->end() |
||
| 218 | ->end() |
||
| 219 | ->end() |
||
| 220 | ->end() |
||
| 221 | ->arrayNode('schedule') |
||
| 222 | ->cannotBeEmpty() |
||
| 223 | ->defaultValue([]) |
||
| 224 | ->requiresAtLeastOneElement() |
||
| 225 | ->useAttributeAsKey('name') |
||
| 226 | ->prototype('array') |
||
| 227 | ->prototype('array') |
||
| 228 | ->children() |
||
| 229 | ->append($this->buildActionArgumentsNode()) |
||
| 230 | ->append($this->addOffsetSection()) |
||
| 231 | ->booleanNode('reschedulable') |
||
| 232 | ->defaultTrue() |
||
| 233 | ->end() |
||
| 234 | ->end() |
||
| 235 | ->end() |
||
| 236 | ->end() |
||
| 237 | ->end() |
||
| 238 | ->scalarNode('expression') |
||
| 239 | ->info('Expression to execute as a event reaction') |
||
| 240 | ->cannotBeEmpty() |
||
| 241 | ->end() |
||
| 242 | ->scalarNode('subject_retrieving_expression') |
||
| 243 | ->info( |
||
| 244 | 'Expression should return subject object for workflow processing '. |
||
| 245 | 'Context variables: event') |
||
| 246 | ->isRequired() |
||
| 247 | ->cannotBeEmpty() |
||
| 248 | ->end() |
||
| 249 | ->end() |
||
| 250 | ->end() |
||
| 251 | ->end() |
||
| 252 | ->end() |
||
| 253 | ; |
||
| 254 | |||
| 255 | return $node; |
||
| 256 | } |
||
| 257 | |||
| 258 | /** |
||
| 259 | * Build prototyped recursive list of arguments of an action |
||
| 260 | * |
||
| 261 | * @param NodeDefinition|null $targetArgumentsNode parent node. If not set it will be created with the 'arguments' name |
||
| 262 | * |
||
| 263 | * @return ArrayNodeDefinition |
||
| 264 | */ |
||
| 265 | private function buildActionArgumentsNode(NodeDefinition $targetArgumentsNode = null) |
||
| 266 | { |
||
| 267 | if (!$targetArgumentsNode) { |
||
| 268 | $builder = new TreeBuilder(); |
||
| 269 | $targetArgumentsNode = $builder->root('arguments'); |
||
| 270 | } |
||
| 271 | |||
| 272 | $targetArgumentsNode |
||
|
0 ignored issues
–
show
It seems like you code against a specific sub-type and not the parent class
Symfony\Component\Config...\Builder\NodeDefinition as the method prototype() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?
Let’s take a look at an example: abstract class User
{
/** @return string */
abstract public function getPassword();
}
class MyUser extends User
{
public function getPassword()
{
// return something
}
public function getDisplayName()
{
// return some name.
}
}
class AuthSystem
{
public function authenticate(User $user)
{
$this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
// do something.
}
}
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break. Available Fixes
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types
inside the if block in such a case.
Loading history...
|
|||
| 273 | ->defaultValue([]) |
||
| 274 | ->prototype('array') |
||
| 275 | ->children() |
||
| 276 | ->enumNode('type') |
||
| 277 | ->values($this->getSupportedActionArgumentTypes()) |
||
| 278 | ->cannotBeEmpty() |
||
| 279 | ->defaultValue(ActionArgumentTypes::TYPE_SCALAR) |
||
| 280 | ->beforeNormalization() |
||
| 281 | ->ifString() |
||
| 282 | ->then(function($v) { |
||
| 283 | return constant(ActionArgumentTypes::class.'::TYPE_'.strtoupper($v)); |
||
| 284 | }) |
||
| 285 | ->end() |
||
| 286 | ->end() |
||
| 287 | ->variableNode('value') |
||
| 288 | ->isRequired() |
||
| 289 | ->cannotBeEmpty() |
||
| 290 | ->end() |
||
| 291 | ->end() |
||
| 292 | ->beforeNormalization() |
||
| 293 | ->ifTrue(function ($v) { |
||
| 294 | return is_scalar($v) || is_array($v) && !ArrayUtils::isArrayAssoc($v); |
||
| 295 | }) |
||
| 296 | ->then(function ($v) { |
||
| 297 | $type = is_scalar($v) ? ActionArgumentTypes::TYPE_SCALAR : ActionArgumentTypes::TYPE_ARRAY; |
||
| 298 | return [ |
||
| 299 | 'type' => $type, |
||
| 300 | 'value' => $v |
||
| 301 | ]; |
||
| 302 | }) |
||
| 303 | ->end() |
||
| 304 | ->validate() |
||
| 305 | ->ifTrue(function ($v) { |
||
| 306 | return is_array($v) && $v['type'] == ActionArgumentTypes::TYPE_ARRAY; |
||
| 307 | }) |
||
| 308 | ->then(function ($v) { |
||
| 309 | // recursive processing of array values |
||
| 310 | $treeBuilder = new TreeBuilder(); |
||
| 311 | $node = $treeBuilder->root('value'); |
||
| 312 | $this->buildActionArgumentsNode($node); |
||
| 313 | $processor = new Processor(); |
||
| 314 | $config = $processor->process($node->getNode(true), [$v['value']]); |
||
| 315 | $v['value'] = $config; |
||
| 316 | |||
| 317 | return $v; |
||
| 318 | }) |
||
| 319 | ->end() |
||
| 320 | ->end() |
||
| 321 | ->validate() |
||
| 322 | ->ifTrue(function ($v) { |
||
| 323 | return ArrayUtils::isArrayAssoc($v, false); |
||
| 324 | }) |
||
| 325 | // To provide cross-platform arguments handling we are not supporting here assoc arrays |
||
| 326 | ->thenInvalid('Only non-associate list of arguments are supported') |
||
| 327 | ->end() |
||
| 328 | ; |
||
| 329 | |||
| 330 | return $targetArgumentsNode; |
||
| 331 | } |
||
| 332 | |||
| 333 | /** |
||
| 334 | * Returns allowed action argument types |
||
| 335 | * |
||
| 336 | * @see ActionArgumentTypes |
||
| 337 | * |
||
| 338 | * @return array |
||
| 339 | */ |
||
| 340 | private function getSupportedActionArgumentTypes() |
||
| 341 | { |
||
| 342 | static $supportedTypes = null; |
||
| 343 | |||
| 344 | if ($supportedTypes === null) { |
||
| 345 | $actionArgumentTypesRef = new ReflectionClass(ActionArgumentTypes::class); |
||
| 346 | $supportedTypes = $actionArgumentTypesRef->getConstants(); |
||
| 347 | } |
||
| 348 | |||
| 349 | return $supportedTypes; |
||
| 350 | } |
||
| 351 | |||
| 352 | /** |
||
| 353 | * Defines scheduler offset config session |
||
| 354 | * |
||
| 355 | * @return NodeDefinition |
||
| 356 | */ |
||
| 357 | private function addOffsetSection() |
||
| 358 | { |
||
| 359 | $builder = new TreeBuilder(); |
||
| 360 | $node = $builder->root('offset', 'scalar'); |
||
| 361 | |||
| 362 | return $node |
||
| 363 | ->isRequired() |
||
| 364 | ->cannotBeEmpty() |
||
| 365 | ->info('Holds period defines offset from time of event catching for transition scheduling. '. |
||
| 366 | 'See https://en.wikipedia.org/wiki/ISO_8601#Durations for format description') |
||
| 367 | ->validate() |
||
| 368 | ->always() |
||
| 369 | ->then(function ($v) { |
||
| 370 | try { |
||
| 371 | new DateInterval($v); |
||
| 372 | |||
| 373 | return $v; |
||
| 374 | } catch (\Exception $e) { |
||
| 375 | throw new InvalidConfigurationException( |
||
| 376 | sprintf('Scheduled transition offset value %s is not valid. '. |
||
| 377 | 'Please use ISO 8601 duration spec. Details: %s', |
||
| 378 | $v, |
||
| 379 | $e->getMessage() |
||
| 380 | ) |
||
| 381 | ); |
||
| 382 | } |
||
| 383 | }) |
||
| 384 | ->end() |
||
| 385 | ; |
||
| 386 | } |
||
| 387 | |||
| 388 | /** |
||
| 389 | * Defines guard config section |
||
| 390 | * |
||
| 391 | * @return ArrayNodeDefinition |
||
| 392 | */ |
||
| 393 | private function addGuardSection() |
||
| 394 | { |
||
| 395 | $builder = new TreeBuilder(); |
||
| 396 | $node = $builder->root('guard'); |
||
| 397 | |||
| 398 | $node |
||
| 399 | ->cannotBeEmpty() |
||
| 400 | ->children() |
||
| 401 | ->scalarNode('expression') |
||
| 402 | ->info('Workflow-level expression that can block or allow transitions. Result should be boolean') |
||
| 403 | ->cannotBeEmpty() |
||
| 404 | ->end() |
||
| 405 | ->arrayNode('transitions') |
||
| 406 | ->useAttributeAsKey('name') |
||
| 407 | ->prototype('array') |
||
| 408 | ->beforeNormalization() |
||
| 409 | ->ifString() |
||
| 410 | ->then(function ($v) { return ['expression' => $v]; }) |
||
| 411 | ->end() |
||
| 412 | ->children() |
||
| 413 | ->scalarNode('expression') |
||
| 414 | ->info('Transition-level expression that can block or allow transition. Result should be boolean') |
||
| 415 | ->isRequired() |
||
| 416 | ->cannotBeEmpty() |
||
| 417 | ->end() |
||
| 418 | ->end() |
||
| 419 | ->end() |
||
| 420 | ->end() |
||
| 421 | ->end() |
||
| 422 | ; |
||
| 423 | |||
| 424 | return $node; |
||
| 425 | } |
||
| 426 | |||
| 427 | /** |
||
| 428 | * Defines subject manipulator config section |
||
| 429 | * |
||
| 430 | * @return ArrayNodeDefinition |
||
| 431 | */ |
||
| 432 | private function addSubjectManipulatorSection() |
||
| 433 | { |
||
| 434 | $builder = new TreeBuilder(); |
||
| 435 | $node = $builder->root('subject_manipulator'); |
||
| 436 | |||
| 437 | $node |
||
| 438 | ->beforeNormalization() |
||
| 439 | ->always() |
||
| 440 | ->then( |
||
| 441 | function ($v) { |
||
| 442 | if (isset($v) && is_array($v)) { |
||
| 443 | foreach ($v as $key => $value) { |
||
| 444 | unset($v[$key]); |
||
| 445 | $key = ltrim($key, "\\"); |
||
| 446 | if (!class_exists($key)) { |
||
| 447 | throw new InvalidConfigurationException( |
||
| 448 | sprintf('Subject class "%s" does not exists', $key) |
||
| 449 | ); |
||
| 450 | } |
||
| 451 | $v[ltrim($key, "\\")] = $value; |
||
| 452 | } |
||
| 453 | } |
||
| 454 | return $v; |
||
| 455 | } |
||
| 456 | ) |
||
| 457 | ->end() |
||
| 458 | ->isRequired() |
||
| 459 | ->cannotBeEmpty() |
||
| 460 | ->useAttributeAsKey('name') |
||
| 461 | ->requiresAtLeastOneElement() |
||
| 462 | ->prototype('array') |
||
| 463 | ->children() |
||
| 464 | ->scalarNode('subject_from_domain') |
||
| 465 | ->cannotBeEmpty() |
||
| 466 | ->info('Expression used to retrieve workflow subject from event triggers workflow actions. '. |
||
| 467 | 'Context variables: subjectId, subjectClass' |
||
| 468 | ) |
||
| 469 | ->end() |
||
| 470 | ->scalarNode('id_from_subject') |
||
| 471 | ->defaultValue('subject.getId()') |
||
| 472 | ->cannotBeEmpty() |
||
| 473 | ->info('Expression used to retrieve subject identifier from subject object. '. |
||
| 474 | 'Identifier can be used later to schedule or reschedule transitions. '. |
||
| 475 | 'Context variables: subject' |
||
| 476 | ) |
||
| 477 | ->end() |
||
| 478 | ->end() |
||
| 479 | ->end() |
||
| 480 | ; |
||
| 481 | |||
| 482 | return $node; |
||
| 483 | } |
||
| 484 | |||
| 485 | /** |
||
| 486 | * Defines scheduler config section |
||
| 487 | * |
||
| 488 | * @return ArrayNodeDefinition |
||
| 489 | */ |
||
| 490 | private function addSchedulerSection() |
||
| 491 | { |
||
| 492 | $builder = new TreeBuilder(); |
||
| 493 | $node = $builder->root('scheduler'); |
||
| 494 | |||
| 495 | $node |
||
| 496 | ->cannotBeEmpty() |
||
| 497 | ->children() |
||
| 498 | ->scalarNode('entity_manager') |
||
| 499 | ->info('Holds entity manager name to persist scheduler jobs') |
||
| 500 | ->defaultValue('default') |
||
| 501 | ->cannotBeEmpty() |
||
| 502 | ->end() |
||
| 503 | ->end() |
||
| 504 | ; |
||
| 505 | |||
| 506 | return $node; |
||
| 507 | } |
||
| 508 | } |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the parent class: