Completed
Push — 1.x ( c183a4...05b51a )
by Alexander
8s
created

ClassProxy   F

Complexity

Total Complexity 66

Size/Duplication

Total Lines 536
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 4

Test Coverage

Coverage 45.54%

Importance

Changes 15
Bugs 3 Features 2
Metric Value
wmc 66
c 15
b 3
f 2
lcom 4
cbo 4
dl 0
loc 536
ccs 92
cts 202
cp 0.4554
rs 3.1913

18 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 56 15
A setParentName() 0 6 1
A override() 0 6 1
A injectJoinPoints() 0 14 2
B wrapWithJoinPoints() 0 33 6
A addInterface() 0 12 4
A addTrait() 0 12 4
A addJoinpointsProperty() 0 8 1
A overrideMethod() 0 7 3
C getJoinpointInvocationBody() 0 24 7
A interceptProperty() 0 5 2
A setMethod() 0 18 2
A setMappings() 0 14 1
A setProperty() 0 13 2
C __toString() 0 31 7
A addFieldInterceptorsCode() 0 10 2
A getOverriddenMethod() 0 18 2
B getConstructorBody() 0 28 4

How to fix   Complexity   

Complex Class

Complex classes like ClassProxy often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ClassProxy, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * Go! AOP framework
4
 *
5
 * @copyright Copyright 2012, Lisachenko Alexander <[email protected]>
6
 *
7
 * This source file is subject to the license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace Go\Proxy;
12
13
use Go\Aop\Advice;
14
use Go\Aop\Features;
15
use Go\Aop\Framework\ClassFieldAccess;
16
use Go\Aop\Framework\MethodInvocationComposer;
17
use Go\Aop\Framework\ReflectionConstructorInvocation;
18
use Go\Aop\Framework\StaticInitializationJoinpoint;
19
use Go\Aop\Intercept\Joinpoint;
20
use Go\Aop\IntroductionInfo;
21
use Go\Core\AspectContainer;
22
use Go\Core\AspectKernel;
23
use Go\Core\LazyAdvisorAccessor;
24
use Reflection;
25
use ReflectionClass;
26
use ReflectionMethod as Method;
27
use ReflectionProperty as Property;
28
use TokenReflection\ReflectionClass as ParsedClass;
29
use TokenReflection\ReflectionMethod as ParsedMethod;
30
use TokenReflection\ReflectionProperty as ParsedProperty;
31
32
/**
33
 * Class proxy builder that is used to generate a child class from the list of joinpoints
34
 */
35
class ClassProxy extends AbstractProxy
36
{
37
    /**
38
     * Parent class reflection
39
     *
40
     * @var null|ParsedClass
41
     */
42
    protected $class = null;
43
44
    /**
45
     * Parent class name, can be changed manually
46
     *
47
     * @var string
48
     */
49
    protected $parentClassName = null;
50
51
    /**
52
     * Source code for methods
53
     *
54
     * @var array Name of method => source code for it
55
     */
56
    protected $methodsCode = [];
57
58
    /**
59
     * Static mappings for class name for excluding if..else check
60
     *
61
     * @var null|array
62
     */
63
    protected static $invocationClassMap = null;
64
65
    /**
66
     * List of additional interfaces to implement
67
     *
68
     * @var array
69
     */
70
    protected $interfaces = [];
71
72
    /**
73
     * List of additional traits for using
74
     *
75
     * @var array
76
     */
77
    protected $traits = [];
78
79
    /**
80
     * Source code for properties
81
     *
82
     * @var array Name of property => source code for it
83
     */
84
    protected $propertiesCode = [];
85
86
    /**
87
     * Name for the current class
88
     *
89
     * @var string
90
     */
91
    protected $name = '';
92
93
    /**
94
     * Flag to determine if we need to add a code for property interceptors
95
     *
96
     * @var bool
97
     */
98
    private $isFieldsIntercepted = false;
99
100
    /**
101
     * List of intercepted properties names
102
     *
103
     * @var array
104
     */
105
    private $interceptedProperties = [];
106
107
    /**
108
     * Generates an child code by parent class reflection and joinpoints for it
109
     *
110
     * @param ParsedClass $parent Parent class reflection
111
     * @param array|Advice[] $classAdvices List of advices for class
112
     *
113
     * @throws \InvalidArgumentException if there are unknown type of advices
114
     */
115 5
    public function __construct(ParsedClass $parent, array $classAdvices)
116
    {
117 5
        parent::__construct($classAdvices);
118
119 5
        $this->class           = $parent;
120 5
        $this->name            = $parent->getShortName();
121 5
        $this->parentClassName = $parent->getShortName();
122
123 5
        $this->addInterface('\Go\Aop\Proxy');
124 5
        $this->addJoinpointsProperty();
125
126 5
        foreach ($classAdvices as $type => $typedAdvices) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
127
128
            switch ($type) {
129 5
                case AspectContainer::METHOD_PREFIX:
130
                case AspectContainer::STATIC_METHOD_PREFIX:
131 5
                    foreach ($typedAdvices as $joinPointName => $advice) {
132 5
                        $method = $parent->getMethod($joinPointName);
133 5
                        if (!$method instanceof ParsedMethod) {
0 ignored issues
show
Bug introduced by
The class TokenReflection\ReflectionMethod does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
134
                            continue;
135
                        }
136 5
                        $this->overrideMethod($method);
137
                    }
138 5
                    break;
139
140
                case AspectContainer::PROPERTY_PREFIX:
141
                    foreach ($typedAdvices as $joinPointName => $advice) {
142
                        $property = $parent->getProperty($joinPointName);
143
                        if (!$property instanceof ParsedProperty) {
0 ignored issues
show
Bug introduced by
The class TokenReflection\ReflectionProperty does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
144
                            continue;
145
                        }
146
                        $this->interceptProperty($property);
147
                    }
148
                    break;
149
150
                case AspectContainer::INTRODUCTION_TRAIT_PREFIX:
151
                    foreach ($typedAdvices as $advice) {
152
                        /** @var $advice IntroductionInfo */
153
                        foreach ($advice->getInterfaces() as $interface) {
154
                            $this->addInterface($interface);
155
                        }
156
                        foreach ($advice->getTraits() as $trait) {
157
                            $this->addTrait($trait);
158
                        }
159
                    }
160
                    break;
161
162
                case AspectContainer::INIT_PREFIX:
163
                case AspectContainer::STATIC_INIT_PREFIX:
164
                    break; // No changes for class
165
166
                default:
167 5
                    throw new \InvalidArgumentException("Unsupported point `$type`");
168
            }
169
        }
170 5
    }
171
172
173
    /**
174
     * Updates parent name for child
175
     *
176
     * @param string $newParentName New class name
177
     *
178
     * @return static
179
     */
180 5
    public function setParentName($newParentName)
181
    {
182 5
        $this->parentClassName = $newParentName;
183
184 5
        return $this;
185
    }
186
187
    /**
188
     * Override parent method with new body
189
     *
190
     * @param string $methodName Method name to override
191
     * @param string $body New body for method
192
     *
193
     * @return static
194
     */
195 5
    public function override($methodName, $body)
196
    {
197 5
        $this->methodsCode[$methodName] = $this->getOverriddenMethod($this->class->getMethod($methodName), $body);
198
199 5
        return $this;
200
    }
201
202
    /**
203
     * Creates a method
204
     *
205
     * @param int $methodFlags See ReflectionMethod modifiers
206
     * @param string $methodName Name of the method
207
     * @param bool $byReference Is method should return value by reference
208
     * @param string $body Body of method
209
     * @param string $parameters Definition of parameters
210
     *
211
     * @return static
212
     */
213
    public function setMethod($methodFlags, $methodName, $byReference, $body, $parameters)
214
    {
215
        $this->methodsCode[$methodName] = (
216
            "/**\n * Method was created automatically, do not change it manually\n */\n" .
217
            join(' ', Reflection::getModifierNames($methodFlags)) . // List of method modifiers
218
            ' function ' . // 'function' keyword
219
            ($byReference ? '&' : '') . // Return value by reference
220
            $methodName . // Method name
221
            '(' . // Start of parameter list
222
            $parameters . // List of parameters
223
            ")\n" . // End of parameter list
224
            "{\n" . // Start of method body
225
            $this->indent($body) . "\n" . // Method body
226
            "}\n" // End of method body
227
        );
228
229
        return $this;
230
    }
231
232
    /**
233
     * Inject advices into given class
234
     *
235
     * NB This method will be used as a callback during source code evaluation to inject joinpoints
236
     *
237
     * @param string $className Aop child proxy class
238
     * @param array|Advice[] $advices List of advices to inject into class
239
     *
240
     * @return void
241
     */
242
    public static function injectJoinPoints($className, array $advices = [])
243
    {
244
        $reflectionClass  = new ReflectionClass($className);
245
        $joinPoints       = static::wrapWithJoinPoints($advices, $reflectionClass->getParentClass()->name);
246
247
        $prop = $reflectionClass->getProperty('__joinPoints');
248
        $prop->setAccessible(true);
249
        $prop->setValue($joinPoints);
250
251
        $staticInit = AspectContainer::STATIC_INIT_PREFIX . ':root';
252
        if (isset($joinPoints[$staticInit])) {
253
            $joinPoints[$staticInit]->__invoke();
254
        }
255
    }
256
257
    /**
258
     * Initialize static mappings to reduce the time for checking features
259
     *
260
     * @param bool $useSplatOperator Enables usage of optimized invocation with splat operator
261
     */
262
    protected static function setMappings($useSplatOperator)
263
    {
264
        $dynamicMethodClass = MethodInvocationComposer::compose(false, $useSplatOperator, false);
265
        $staticMethodClass  = MethodInvocationComposer::compose(true, $useSplatOperator, false);
266
267
        // We are using LSB here and overridden static property
268
        static::$invocationClassMap = array(
269
            AspectContainer::METHOD_PREFIX        => $dynamicMethodClass,
270
            AspectContainer::STATIC_METHOD_PREFIX => $staticMethodClass,
271
            AspectContainer::PROPERTY_PREFIX      => ClassFieldAccess::class,
272
            AspectContainer::STATIC_INIT_PREFIX   => StaticInitializationJoinpoint::class,
273
            AspectContainer::INIT_PREFIX          => ReflectionConstructorInvocation::class
274
        );
275
    }
276
277
    /**
278
     * Wrap advices with joinpoint object
279
     *
280
     * @param array|Advice[] $classAdvices Advices for specific class
281
     * @param string $className Name of the original class to use
282
     *
283
     * @throws \UnexpectedValueException If joinPoint type is unknown
284
     *
285
     * NB: Extension should be responsible for wrapping advice with join point.
286
     *
287
     * @return array|Joinpoint[] returns list of joinpoint ready to use
288
     */
289
    protected static function wrapWithJoinPoints($classAdvices, $className)
290
    {
291
        /** @var LazyAdvisorAccessor $accessor */
292
        static $accessor = null;
293
294
        if (!self::$invocationClassMap) {
295
            $aspectKernel = AspectKernel::getInstance();
296
            $accessor     = $aspectKernel->getContainer()->get('aspect.advisor.accessor');
297
            self::setMappings(
298
                $aspectKernel->hasFeature(Features::USE_SPLAT_OPERATOR)
299
            );
300
        }
301
302
        $joinPoints = [];
303
304
        foreach ($classAdvices as $joinPointType => $typedAdvices) {
305
            // if not isset then we don't want to create such invocation for class
306
            if (!isset(self::$invocationClassMap[$joinPointType])) {
307
                continue;
308
            }
309
            foreach ($typedAdvices as $joinPointName => $advices) {
310
                $filledAdvices = [];
311
                foreach ($advices as $advisorName) {
312
                    $filledAdvices[] = $accessor->$advisorName;
313
                }
314
315
                $joinpoint = new self::$invocationClassMap[$joinPointType]($className, $joinPointName, $filledAdvices);
316
                $joinPoints["$joinPointType:$joinPointName"] = $joinpoint;
317
            }
318
        }
319
320
        return $joinPoints;
321
    }
322
323
    /**
324
     * Add an interface for child
325
     *
326
     * @param string|ReflectionClass|ParsedClass $interface
327
     *
328
     * @throws \InvalidArgumentException If object is not an interface
329
     */
330 5
    public function addInterface($interface)
331
    {
332 5
        $interfaceName = $interface;
333 5
        if ($interface instanceof ReflectionClass || $interface instanceof ParsedClass) {
0 ignored issues
show
Bug introduced by
The class TokenReflection\ReflectionClass does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
334
            if (!$interface->isInterface()) {
335
                throw new \InvalidArgumentException("Interface expected to add");
336
            }
337
            $interfaceName = $interface->name;
338
        }
339
        // Use absolute namespace to prevent NS-conflicts
340 5
        $this->interfaces[] = '\\' . ltrim($interfaceName, '\\');
341 5
    }
342
343
    /**
344
     * Add a trait for child
345
     *
346
     * @param string|ReflectionClass|ParsedClass $trait
347
     *
348
     * @throws \InvalidArgumentException If object is not a trait
349
     */
350
    public function addTrait($trait)
351
    {
352
        $traitName = $trait;
353
        if ($trait instanceof ReflectionClass || $trait instanceof ParsedClass) {
0 ignored issues
show
Bug introduced by
The class TokenReflection\ReflectionClass does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
354
            if (!$trait->isTrait()) {
355
                throw new \InvalidArgumentException("Trait expected to add");
356
            }
357
            $traitName = $trait->name;
358
        }
359
        // Use absolute namespace to prevent NS-conflicts
360
        $this->traits[] = '\\' . ltrim($traitName, '\\');
361
    }
362
363
    /**
364
     * Creates a property
365
     *
366
     * @param int $propFlags See ReflectionProperty modifiers
367
     * @param string $propName Name of the property
368
     * @param null|string $defaultText Default value, should be string text!
369
     *
370
     * @return static
371
     */
372 5
    public function setProperty($propFlags, $propName, $defaultText = null)
373
    {
374 5
        $this->propertiesCode[$propName] = (
375
            "/**\n * Property was created automatically, do not change it manually\n */\n" . // Doc-block
376 5
            join(' ', Reflection::getModifierNames($propFlags)) . // List of modifiers for property
377 5
            ' $' . // Space and vaiable symbol
378 5
            $propName . // Name of the property
379 5
            (is_string($defaultText) ? " = $defaultText" : '') . // Default value if present
380 5
            ";\n" // End of line with property definition
381
        );
382
383 5
        return $this;
384
    }
385
386
    /**
387
     * Adds a definition for joinpoints private property in the class
388
     *
389
     * @return void
390
     */
391 5
    protected function addJoinpointsProperty()
392
    {
393 5
        $this->setProperty(
394 5
            Property::IS_PRIVATE | Property::IS_STATIC,
395 5
            '__joinPoints',
396 5
            '[]'
397
        );
398 5
    }
399
400
    /**
401
     * Override parent method with joinpoint invocation
402
     *
403
     * @param ParsedMethod $method Method reflection
404
     */
405 5
    protected function overrideMethod(ParsedMethod $method)
406
    {
407
        // temporary disable override of final methods
408 5
        if (!$method->isFinal() && !$method->isAbstract()) {
409 5
            $this->override($method->name, $this->getJoinpointInvocationBody($method));
410
        }
411 5
    }
412
413
    /**
414
     * Creates definition for method body
415
     *
416
     * @param ParsedMethod $method Method reflection
417
     *
418
     * @return string new method body
419
     */
420 5
    protected function getJoinpointInvocationBody(ParsedMethod $method)
421
    {
422 5
        $isStatic = $method->isStatic();
423 5
        $scope    = $isStatic ? self::$staticLsbExpression : '$this';
424 5
        $prefix   = $isStatic ? AspectContainer::STATIC_METHOD_PREFIX : AspectContainer::METHOD_PREFIX;
425
426 5
        $args = $this->prepareArgsLine($method);
427 5
        $body = '';
428
429 5
        if (($this->class->name === $method->getDeclaringClassName()) && strpos($method->getSource(), 'func_get_args') !== false) {
430 2
            $body = '$argsList = \func_get_args();' . PHP_EOL;
431 2
            if (empty($args)) {
432
                $scope = "$scope, \$argsList";
433
            } else {
434 2
                $scope = "$scope, [$args] + \$argsList";
435
            }
436 5
        } elseif (!empty($args)) {
437 2
            $scope = "$scope, [$args]";
438
        }
439
440 5
        $body .= "return self::\$__joinPoints['{$prefix}:{$method->name}']->__invoke($scope);";
441
442 5
        return $body;
443
    }
444
445
    /**
446
     * Makes property intercepted
447
     *
448
     * @param ParsedProperty $property Reflection of property to intercept
449
     */
450
    protected function interceptProperty(ParsedProperty $property)
451
    {
452
        $this->interceptedProperties[] = is_object($property) ? $property->name : $property;
453
        $this->isFieldsIntercepted = true;
454
    }
455
456
    /**
457
     * {@inheritDoc}
458
     */
459 5
    public function __toString()
460
    {
461 5
        $ctor = $this->class->getConstructor();
462 5
        if ($this->isFieldsIntercepted && (!$ctor || !$ctor->isPrivate())) {
463
            $this->addFieldInterceptorsCode($ctor);
464
        }
465
466 5
        $prefix = join(' ', Reflection::getModifierNames($this->class->getModifiers()));
467
468
        $classCode = (
469 5
            $this->class->getDocComment() . "\n" . // Original doc-block
470 5
            ($prefix ? "$prefix " : '') . // List of class modifiers
471 5
            'class ' . // 'class' keyword with one space
472 5
            $this->name . // Name of the class
473 5
            ' extends ' . // 'extends' keyword with
474 5
            $this->parentClassName . // Name of the parent class
475 5
            ($this->interfaces ? ' implements ' . join(', ', $this->interfaces) : '') . "\n" . // Interfaces list
476 5
            "{\n" . // Start of class definition
477 5
            ($this->traits ? $this->indent('use ' . join(', ', $this->traits) . ';' . "\n") : '') . "\n" . // Traits list
478 5
            $this->indent(join("\n", $this->propertiesCode)) . "\n" . // Property definitions
479 5
            $this->indent(join("\n", $this->methodsCode)) . "\n" . // Method definitions
480 5
            "}" // End of class definition
481
        );
482
483
        return $classCode
484
            // Inject advices on call
485 5
            . PHP_EOL
486 5
            . '\\' . __CLASS__ . "::injectJoinPoints('"
487 5
                . $this->class->name . "',"
488 5
                . var_export($this->advices, true) . ");";
489
    }
490
491
    /**
492
     * Add code for intercepting properties
493
     *
494
     * @param null|ParsedMethod $constructor Constructor reflection or null
495
     */
496
    protected function addFieldInterceptorsCode(ParsedMethod $constructor = null)
497
    {
498
        $this->addTrait(PropertyInterceptionTrait::class);
499
        $this->isFieldsIntercepted = true;
500
        if ($constructor) {
501
            $this->override('__construct', $this->getConstructorBody($constructor, true));
502
        } else {
503
            $this->setMethod(Method::IS_PUBLIC, '__construct', false, $this->getConstructorBody(), '');
504
        }
505
    }
506
507
    /**
508
     * Creates a method code from Reflection
509
     *
510
     * @param ParsedMethod $method Reflection for method
511
     * @param string $body Body of method
512
     *
513
     * @return string
514
     */
515 5
    protected function getOverriddenMethod(ParsedMethod $method, $body)
516
    {
517
        $code = (
518 5
            preg_replace('/ {4}|\t/', '', $method->getDocComment()) . "\n" . // Original Doc-block
519 5
            join(' ', Reflection::getModifierNames($method->getModifiers())) . // List of modifiers
520 5
            ' function ' . // 'function' keyword
521 5
            ($method->returnsReference() ? '&' : '') . // By reference symbol
522 5
            $method->name . // Name of the method
523 5
            '(' . // Start of parameters list
524 5
            join(', ', $this->getParameters($method->getParameters())) . // List of parameters
525 5
            ")\n" . // End of parameters list
526 5
            "{\n" . // Start of method body
527 5
            $this->indent($body) . "\n" . // Method body
528 5
            "}\n" // End of method body
529
        );
530
531 5
        return $code;
532
    }
533
534
    /**
535
     * Returns constructor code
536
     *
537
     * @param ParsedMethod $constructor Constructor reflection
538
     * @param bool $isCallParent Is there is a need to call parent code
539
     *
540
     * @return string
541
     */
542
    private function getConstructorBody(ParsedMethod $constructor = null, $isCallParent = false)
543
    {
544
        $assocProperties = [];
545
        $listProperties  = [];
546
        foreach ($this->interceptedProperties as $propertyName) {
547
            $assocProperties[] = "'$propertyName' => &\$this->$propertyName";
548
            $listProperties[]  = "\$this->$propertyName";
549
        }
550
        $assocProperties = $this->indent(join(',' . PHP_EOL, $assocProperties));
551
        $listProperties  = $this->indent(join(',' . PHP_EOL, $listProperties));
552
        if (isset($this->methodsCode['__construct'])) {
553
            $parentCall = $this->getJoinpointInvocationBody($constructor);
0 ignored issues
show
Bug introduced by
It seems like $constructor defined by parameter $constructor on line 542 can be null; however, Go\Proxy\ClassProxy::getJoinpointInvocationBody() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
554
        } elseif ($isCallParent) {
555
            $parentCall = '\call_user_func_array(["parent", __FUNCTION__], \func_get_args());';
556
        } else {
557
            $parentCall = '';
558
        }
559
560
        return <<<CTOR
561
\$this->__properties = array(
562
$assocProperties
563
);
564
unset(
565
$listProperties
566
);
567
$parentCall
568
CTOR;
569
    }
570
}
571