FunctionDefinition   F
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 619
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
wmc 60
lcom 2
cbo 6
dl 0
loc 619
rs 3.581
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
A getDocBlock() 0 4 1
A isFinal() 0 4 1
A isAbstract() 0 4 1
A getVisibility() 0 4 1
A isStatic() 0 4 1
A getName() 0 4 1
A getParameterDefinitions() 0 4 1
A getPreconditions() 0 4 1
A getAncestralPreconditions() 0 4 1
A getBody() 0 4 1
A getPointcutExpressions() 0 4 1
A getPostconditions() 0 4 1
A getAncestralPostconditions() 0 4 1
A getStructureName() 0 4 1
B getAllPreconditions() 0 32 9
A getAllPostconditions() 0 21 5
C getHeader() 0 72 11
A conditionIsMismatch() 0 21 4
A setDocBlock() 0 4 1
A setIsFinal() 0 4 1
A setIsAbstract() 0 4 1
A setIsStatic() 0 4 1
A setName() 0 4 1
A setParameterDefinitions() 0 4 1
A setPreconditions() 0 4 1
A setAncestralPreconditions() 0 4 1
A setPointcutExpressions() 0 4 1
A setPostconditions() 0 4 1
A setAncestralPostconditions() 0 4 1
A setUsesOld() 0 4 1
A setBody() 0 4 1
A setStructureName() 0 4 1
A setVisibility() 0 4 1
A usesOld() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like FunctionDefinition 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 FunctionDefinition, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * \AppserverIo\Doppelgaenger\Entities\Definitions\FunctionDefinition
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Bernhard Wick <[email protected]>
15
 * @copyright 2015 TechDivision GmbH - <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/appserver-io/doppelgaenger
18
 * @link      http://www.appserver.io/
19
 */
20
21
namespace AppserverIo\Doppelgaenger\Entities\Definitions;
22
23
use AppserverIo\Doppelgaenger\Entities\Lists\AssertionList;
24
use AppserverIo\Doppelgaenger\Entities\Lists\ParameterDefinitionList;
25
use AppserverIo\Doppelgaenger\Entities\Lists\PointcutExpressionList;
26
use AppserverIo\Doppelgaenger\Entities\Lists\TypedListList;
27
use AppserverIo\Doppelgaenger\Entities\Lists\PointcutList;
28
use AppserverIo\Psr\MetaobjectProtocol\Dbc\Assertions\AssertionInterface;
29
30
/**
31
 * Provides a definition of a (generally speaking) function.
32
 * This includes methods as well
33
 *
34
 * @author    Bernhard Wick <[email protected]>
35
 * @copyright 2015 TechDivision GmbH - <[email protected]>
36
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
37
 * @link      https://github.com/appserver-io/doppelgaenger
38
 * @link      http://www.appserver.io/
39
 */
40
class FunctionDefinition extends AbstractDefinition
41
{
42
43
    /**
44
     * @var string $docBlock DocBlock comment of the function
45
     */
46
    protected $docBlock;
47
48
    /**
49
     * @var boolean $isFinal Is the function final?
50
     */
51
    protected $isFinal;
52
53
    /**
54
     * @var boolean $isAbstract Is the function abstract?
55
     */
56
    protected $isAbstract;
57
58
    /**
59
     * @var string $visibility Visibility of the method
60
     */
61
    protected $visibility;
62
63
    /**
64
     * @var boolean $isStatic Is the method static?
65
     */
66
    protected $isStatic;
67
68
    /**
69
     * @var string $name Name of the function
70
     */
71
    protected $name;
72
73
    /**
74
     * @var \AppserverIo\Doppelgaenger\Entities\Lists\ParameterDefinitionList $parameterDefinitions List of parameter definitions
75
     */
76
    protected $parameterDefinitions;
77
78
    /**
79
     * Lists of pointcuts
80
     *
81
     * @var \AppserverIo\Doppelgaenger\Entities\Lists\PointcutExpressionList $pointcuts
82
     */
83
    protected $pointcutExpressions;
84
85
    /**
86
     * @var \AppserverIo\Doppelgaenger\Entities\Lists\AssertionList $preconditions Preconditions of this function
87
     */
88
    protected $preconditions;
89
90
    /**
91
     * @var \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList $ancestralPreconditions Preconditions of any parent functions
92
     */
93
    protected $ancestralPreconditions;
94
95
    /**
96
     * @var boolean $usesOld Does this function use the dgOld keyword?
97
     */
98
    protected $usesOld;
99
100
    /**
101
     * @var string $body Body of the function
102
     */
103
    protected $body;
104
105
    /**
106
     * @var \AppserverIo\Doppelgaenger\Entities\Lists\AssertionList $postconditions Postconditions of this function
107
     */
108
    protected $postconditions;
109
110
    /**
111
     * @var \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList $ancestralPostconditions
112
     *          Postconditions of any parent functions
113
     */
114
    protected $ancestralPostconditions;
115
116
    /**
117
     * Name of the structure containing that function
118
     *
119
     * @var string $structureName
120
     */
121
    protected $structureName;
122
123
    /**
124
     * Default constructor
125
     */
126
    public function __construct()
127
    {
128
        $this->docBlock = '';
129
        $this->isFinal = false;
130
        $this->isAbstract = false;
131
        $this->visibility = '';
132
        $this->isStatic = false;
133
        $this->name = '';
134
        $this->parameterDefinitions = new ParameterDefinitionList();
135
        $this->preconditions = new AssertionList();
136
        $this->ancestralPreconditions = new TypedListList();
137
        $this->usesOld = false;
138
        $this->body = '';
139
        $this->postconditions = new AssertionList();
140
        $this->ancestralPostconditions = new TypedListList();
141
        $this->pointcutExpressions = new PointcutExpressionList();
142
        $this->structureName = '';
143
    }
144
145
    /**
146
     * Getter method for attribute $docBlock
147
     *
148
     * @return string
149
     */
150
    public function getDocBlock()
151
    {
152
        return $this->docBlock;
153
    }
154
155
    /**
156
     * Getter method for attribute $isFinal
157
     *
158
     * @return boolean
159
     */
160
    public function isFinal()
161
    {
162
        return $this->isFinal;
163
    }
164
165
    /**
166
     * Getter method for attribute $isAbstract
167
     *
168
     * @return boolean
169
     */
170
    public function isAbstract()
171
    {
172
        return $this->isAbstract;
173
    }
174
175
    /**
176
     * Getter method for attribute $visibility
177
     *
178
     * @return string
179
     */
180
    public function getVisibility()
181
    {
182
        return $this->visibility;
183
    }
184
185
    /**
186
     * Getter method for attribute $isStatic
187
     *
188
     * @return boolean
189
     */
190
    public function isStatic()
191
    {
192
        return $this->isStatic;
193
    }
194
195
    /**
196
     * Getter method for attribute $name
197
     *
198
     * @return string
199
     */
200
    public function getName()
201
    {
202
        return $this->name;
203
    }
204
205
    /**
206
     * Getter method for attribute $parameterDefinitions
207
     *
208
     * @return ParameterDefinitionList
209
     */
210
    public function getParameterDefinitions()
211
    {
212
        return $this->parameterDefinitions;
213
    }
214
215
    /**
216
     * Getter method for attribute $preconditions
217
     *
218
     * @return AssertionList
219
     */
220
    public function getPreconditions()
221
    {
222
        return $this->preconditions;
223
    }
224
225
    /**
226
     * Getter method for attribute $ancestralPreconditions
227
     *
228
     * @return null|TypedListList
229
     */
230
    public function getAncestralPreconditions()
231
    {
232
        return $this->ancestralPreconditions;
233
    }
234
235
    /**
236
     * Getter method for attribute $body
237
     *
238
     * @return string
239
     */
240
    public function getBody()
241
    {
242
        return $this->body;
243
    }
244
245
    /**
246
     * Getter method for attribute $pointcutExpressions
247
     *
248
     * @return \AppserverIo\Doppelgaenger\Entities\Lists\PointcutExpressionList
249
     */
250
    public function getPointcutExpressions()
251
    {
252
        return $this->pointcutExpressions;
253
    }
254
255
    /**
256
     * Getter method for attribute $postconditions
257
     *
258
     * @return AssertionList
259
     */
260
    public function getPostconditions()
261
    {
262
        return $this->postconditions;
263
    }
264
265
    /**
266
     * Getter method for attribute $ancestralPostconditions
267
     *
268
     * @return null|TypedListList
269
     */
270
    public function getAncestralPostconditions()
271
    {
272
        return $this->ancestralPreconditions;
273
    }
274
275
    /**
276
     * Getter method for attribute $structureName
277
     *
278
     * @return string
279
     */
280
    public function getStructureName()
281
    {
282
        return $this->structureName;
283
    }
284
285
    /**
286
     * Will return all preconditions. Direct as well as ancestral.
287
     *
288
     * @param boolean $nonPrivateOnly   Make this true if you only want conditions which do not have a private context
289
     * @param boolean $filterMismatches Do we have to filter condition mismatches due to signature changes
290
     *
291
     * @return \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList
292
     */
293
    public function getAllPreconditions($nonPrivateOnly = false, $filterMismatches = true)
294
    {
295
        $preconditions = clone $this->ancestralPreconditions;
296
        $preconditions->add($this->preconditions);
297
298
        // If we need to we will filter all the non private conditions from the lists
299
        // Preconditions have to be flattened as the signature of a function (and therefore it's parameter list)
300
        // might change within a structure hierarchy.
301
        // We have to do that here, as we cannot risk to delete conditions which use non existing parameters, as
302
        // a potential child method might want to inherit grandparental conditions which do not make sense for us
303
        // (but do for them).
304
        if ($nonPrivateOnly === true || $filterMismatches === true) {
305
            $preconditionListIterator = $preconditions->getIterator();
306
            foreach ($preconditionListIterator as $preconditionList) {
307
                $preconditionIterator = $preconditionList->getIterator();
308
                foreach ($preconditionIterator as $key => $precondition) {
309
                    // The privacy issue
310
                    if ($nonPrivateOnly === true && $precondition->isPrivateContext()) {
311
                        $preconditionList->delete($key);
312
                    }
313
314
                    // The mismatch filter
315
                    if ($filterMismatches === true && $this->conditionIsMismatch($precondition)) {
316
                        $preconditionList->delete($key);
317
                    }
318
                }
319
            }
320
        }
321
322
        // Return what is left
323
        return $preconditions;
324
    }
325
326
    /**
327
     * Will return all postconditions. Direct as well as ancestral.
328
     *
329
     * @param boolean $nonPrivateOnly Make this true if you only want conditions which do not have a private context
330
     *
331
     * @return \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList
332
     */
333
    public function getAllPostconditions($nonPrivateOnly = false)
334
    {
335
        $postconditions = clone $this->ancestralPostconditions;
336
        $postconditions->add($this->postconditions);
337
338
        // If we need to we will filter all the non private conditions from the lists
339
        if ($nonPrivateOnly === true) {
340
            $postconditionListIterator = $postconditions->getIterator();
341
            foreach ($postconditionListIterator as $postconditionList) {
342
                $postconditionIterator = $postconditionList->getIterator();
343
                foreach ($postconditionIterator as $key => $postcondition) {
344
                    if ($postcondition->isPrivateContext()) {
345
                        $postconditionList->delete($key);
346
                    }
347
                }
348
            }
349
        }
350
351
        // Return what is left
352
        return $postconditions;
353
    }
354
355
    /**
356
     * Will return the header of this function either in calling or in defining manner.
357
     * String will stop after the closing ")" bracket, so the string can be used for interfaces as well.
358
     *
359
     * @param string  $type        Can be either "call" or "definition"
360
     * @param string  $suffix      Suffix for the function name
361
     * @param boolean $showMe      Will mark a method as original by extending it with a suffix
362
     * @param boolean $forceStatic Will force static call for call type headers
363
     *
364
     * @return  string
365
     */
366
    public function getHeader($type, $suffix = '', $showMe = false, $forceStatic = false)
367
    {
368
        $header = '';
369
370
        // We have to do some more work if we need the definition header
371
        if ($type === 'definition') {
372
            // Check for final or abstract (abstract cannot be used if final)
373
            if ($this->isFinal) {
374
                $header .= ' final ';
375
            } elseif ($this->isAbstract) {
376
                $header .= ' abstract ';
377
            }
378
379
            // Do we need to make this function public? If not we will use the original visibility
380
            if ($showMe === false) {
381
                // Prepend visibility
382
                $header .= $this->visibility;
383
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
384
            } else {
385
                $header .= 'public';
386
            }
387
388
            // Are we static?
389
            if ($this->isStatic) {
390
                $header .= ' static ';
391
            }
392
393
            // Function keyword and name
394
            $header .= ' function ';
395
        }
396
397
        // If we have to generate code for a call we have to check for either static or normal access
398
        if ($type === 'call') {
399
            if ($this->isStatic === true || $forceStatic) {
400
                $header .= 'self::';
401
            } else {
402
                $header .= '$this->';
403
            }
404
        }
405
406
        // Function name + the suffix we might have gotten
407
        $header .= $this->name . $suffix;
408
409
        // Iterate over all parameters and create the parameter string.
410
        // We will create the string we need, either for calling the function or for defining it.
411
        $parameterString = array();
412
        $parameterIterator = $this->parameterDefinitions->getIterator();
413
        for ($k = 0; $k < $parameterIterator->count(); $k++) {
414
            // Our parameter
415
            $parameter = $parameterIterator->current();
416
417
            // Fill our strings
418
            $parameterString[] = $parameter->getString($type);
419
420
            // Next assertion please
421
            $parameterIterator->next();
422
        }
423
424
        // Check if we even got something. If not a closure header would be malformed.
425
        if (!empty($parameterString)) {
426
            // Explode to insert commas
427
            $parameterString = implode(', ', $parameterString);
428
429
            // Append the parameters to the header
430
            $header .= '(' . $parameterString . ')';
431
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
432
        } else {
433
            $header .= '()';
434
        }
435
436
        return $header;
437
    }
438
439
    /**
440
     * This method will check if a certain assertion mismatches the scope of this function.
441
     *
442
     * @param \AppserverIo\Psr\MetaobjectProtocol\Dbc\Assertions\AssertionInterface $assertion The assertion to check for a possible mismatch
443
     *          within this function context
444
     *
445
     * @return boolean
446
     */
447
    protected function conditionIsMismatch(AssertionInterface $assertion)
448
    {
449
        // If the minimal scope is above or below function scope we cannot determine if this is a mismatch in
450
        // function scope.
451
        if ($assertion->getMinScope() !== 'function') {
452
            return false;
453
        }
454
455
        // We will get all parameters and check if we can find any of it in the assertion string.
456
        // If not then we have a mismatch as the condition is only function scoped
457
        $assertionString = $assertion->getString();
458
        $parameterIterator = $this->parameterDefinitions->getIterator();
459
        foreach ($parameterIterator as $parameter) {
460
            if (strpos($assertionString, $parameter->name) !== false) {
461
                return false;
462
            }
463
        }
464
465
        // Still here, that does not sound good
466
        return true;
467
    }
468
469
    /**
470
     * Setter method for attribute $docBlock
471
     *
472
     * @param string $docBlock Doc block of the structure
473
     *
474
     * @return null
475
     */
476
    public function setDocBlock($docBlock)
477
    {
478
        $this->docBlock = $docBlock;
479
    }
480
481
    /**
482
     * Setter method for the $isFinal property
483
     *
484
     * @param boolean $isFinal If the class is defined final
485
     *
486
     * @return null
487
     */
488
    public function setIsFinal($isFinal)
489
    {
490
        $this->isFinal = $isFinal;
491
    }
492
493
    /**
494
     * Setter method for the $isAbstract property
495
     *
496
     * @param boolean $isAbstract If the class is abstract
497
     *
498
     * @return null
499
     */
500
    public function setIsAbstract($isAbstract)
501
    {
502
        $this->isAbstract = $isAbstract;
503
    }
504
505
    /**
506
     * Setter method for the $isStatic property
507
     *
508
     * @param boolean $isStatic If the attribute is declared static
509
     *
510
     * @return null
511
     */
512
    public function setIsStatic($isStatic)
513
    {
514
        $this->isStatic = $isStatic;
515
    }
516
517
    /**
518
     * Setter method for attribute $name
519
     *
520
     * @param string $name Name of the structure
521
     *
522
     * @return null
523
     */
524
    public function setName($name)
525
    {
526
        $this->name = $name;
527
    }
528
529
    /**
530
     * Setter method for attribute $parameterDefinitions
531
     *
532
     * @param \AppserverIo\Doppelgaenger\Entities\Lists\ParameterDefinitionList $parameterDefinitions List of parameters
533
     *
534
     * @return null
535
     */
536
    public function setParameterDefinitions(ParameterDefinitionList $parameterDefinitions)
537
    {
538
        $this->parameterDefinitions = $parameterDefinitions;
539
    }
540
541
    /**
542
     * Setter method for attribute $preconditions
543
     *
544
     * @param \AppserverIo\Doppelgaenger\Entities\Lists\AssertionList $preconditions List of preconditions
545
     *
546
     * @return null
547
     */
548
    public function setPreconditions(AssertionList $preconditions)
549
    {
550
        $this->preconditions = $preconditions;
551
    }
552
553
    /**
554
     * Setter method for attribute $ancestralPreconditions
555
     *
556
     * @param \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList $ancestralPreconditions Inherited preconditions
557
     *
558
     * @return null
559
     */
560
    public function setAncestralPreconditions(TypedListList $ancestralPreconditions)
561
    {
562
        $this->ancestralPreconditions = $ancestralPreconditions;
563
    }
564
565
    /**
566
     * Setter method for attribute $pointcutExpressions
567
     *
568
     * @param \AppserverIo\Doppelgaenger\Entities\Lists\PointcutExpressionList $pointcutExpressions List of pointcut expressions
569
     *
570
     * @return null
571
     */
572
    public function setPointcutExpressions(PointcutExpressionList $pointcutExpressions)
573
    {
574
        $this->pointcutExpressions = $pointcutExpressions;
575
    }
576
577
    /**
578
     * Getter method for attribute $postconditions
579
     *
580
     * @param \AppserverIo\Doppelgaenger\Entities\Lists\AssertionList $postconditions List of postconditions
581
     *
582
     * @return null
583
     */
584
    public function setPostconditions(AssertionList $postconditions)
585
    {
586
        $this->postconditions = $postconditions;
587
    }
588
589
    /**
590
     * Setter method for attribute $ancestralPostconditions
591
     *
592
     * @param \AppserverIo\Doppelgaenger\Entities\Lists\TypedListList $ancestralPreconditions Inherited preconditions
593
     *
594
     * @return null
595
     */
596
    public function setAncestralPostconditions(TypedListList $ancestralPreconditions)
597
    {
598
        $this->ancestralPreconditions = $ancestralPreconditions;
599
    }
600
601
    /**
602
     * Stter method for attribute $usesOld
603
     *
604
     * @param boolean $usesOld Does the function use the "old" keyword
605
     *
606
     * @return null
607
     */
608
    public function setUsesOld($usesOld)
609
    {
610
        $this->usesOld = $usesOld;
611
    }
612
613
    /**
614
     * Getter method for attribute $body
615
     *
616
     * @param string $body Body of the function
617
     *
618
     * @return null
619
     */
620
    public function setBody($body)
621
    {
622
        $this->body = $body;
623
    }
624
625
    /**
626
     * Setter method for attribute $structureName
627
     *
628
     * @param string $structureName Name of the structure containing that function
629
     *
630
     * @return null
631
     */
632
    public function setStructureName($structureName)
633
    {
634
        $this->structureName = $structureName;
635
    }
636
637
    /**
638
     * Setter method for the $visibility property
639
     *
640
     * @param string $visibility Visibility of the attribute
641
     *
642
     * @return null
643
     */
644
    public function setVisibility($visibility)
645
    {
646
        $this->visibility = $visibility;
647
    }
648
649
    /**
650
     * Getter method for attribute $usesOld
651
     *
652
     * @return boolean
653
     */
654
    public function usesOld()
655
    {
656
        return $this->usesOld;
657
    }
658
}
659