Completed
Push — master ( 99fc91...efe256 )
by Alexander
9s
created

AbstractInterceptor::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 3
crap 1
1
<?php
2
declare(strict_types = 1);
3
/*
4
 * Go! AOP framework
5
 *
6
 * @copyright Copyright 2011, Lisachenko Alexander <[email protected]>
7
 *
8
 * This source file is subject to the license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Go\Aop\Framework;
13
14
use Closure;
15
use Go\Core\AspectKernel;
16
use ReflectionFunction;
17
use ReflectionMethod;
18
use Serializable;
19
use Go\Aop\Intercept\Interceptor;
20
21
/**
22
 * Base class for all framework interceptor implementations
23
 *
24
 * This class describe an action taken by the interceptor at a particular joinpoint.
25
 * Different types of interceptors include "around", "before" and "after" advices.
26
 *
27
 * Around interceptor is an advice that surrounds a joinpoint such as a method invocation. This is the most powerful
28
 * kind of advice. Around advices will perform custom behavior before and after the method invocation. They are
29
 * responsible for choosing whether to proceed to the joinpoint or to shortcut executing by returning their own return
30
 * value or throwing an exception.
31
 *
32
 * After and before interceptors are simple closures that will be invoked after and before main invocation.
33
 *
34
 * Framework models an interceptor as an PHP-closure, maintaining a chain of interceptors "around" the joinpoint:
35
 *   public function (Joinpoint $joinPoint)
36
 *   {
37
 *      echo 'Before action';
38
 *      // call chain here with Joinpoint->proceed() method
39
 *      $result = $joinPoint->proceed();
40
 *      echo 'After action';
41
 *
42
 *      return $result;
43
 *   }
44
 */
45
abstract class AbstractInterceptor implements Interceptor, OrderedAdvice, Serializable
46
{
47
    /**
48
     * Local cache of advices for faster unserialization on big projects
49
     *
50
     * @var Closure[]
51
     */
52
    protected static $localAdvicesCache = [];
53
54
    /**
55
     * Pointcut expression string which was used for this interceptor
56
     */
57
    protected $pointcutExpression;
58
59
    /**
60
     * Closure to call
61
     */
62
    protected $adviceMethod;
63
64
    /**
65
     * Advice order
66
     */
67
    private $adviceOrder;
68
69
    /**
70
     * Default constructor for interceptor
71
     */
72 11
    public function __construct(Closure $adviceMethod, int $adviceOrder = 0, string $pointcutExpression = '')
73
    {
74 11
        $this->adviceMethod       = $adviceMethod;
75 11
        $this->adviceOrder        = $adviceOrder;
76 11
        $this->pointcutExpression = $pointcutExpression;
77 11
    }
78
79
    /**
80
     * Serialize advice method into array
81
     */
82
    public static function serializeAdvice(Closure $adviceMethod): array
83
    {
84
        $refAdvice = new ReflectionFunction($adviceMethod);
85
86
        return [
87
            'method' => $refAdvice->name,
88
            'aspect' => get_class($refAdvice->getClosureThis())
89
        ];
90
    }
91
92
    /**
93
     * Unserialize an advice
94
     *
95
     * @param array $adviceData Information about advice
96
     */
97
    public static function unserializeAdvice(array $adviceData): Closure
98
    {
99
        $aspectName = $adviceData['aspect'];
100
        $methodName = $adviceData['method'];
101
102
        if (!isset(static::$localAdvicesCache["$aspectName->$methodName"])) {
103
            $aspect    = AspectKernel::getInstance()->getContainer()->getAspect($aspectName);
104
            $refMethod = new ReflectionMethod($aspectName, $methodName);
105
            $advice    = $refMethod->getClosure($aspect);
106
107
            static::$localAdvicesCache["$aspectName->$methodName"] = $advice;
108
        }
109
110
        return static::$localAdvicesCache["$aspectName->$methodName"];
111
    }
112
113
    /**
114
     * Returns the advice order
115
     */
116
    public function getAdviceOrder(): int
117
    {
118
        return $this->adviceOrder;
119
    }
120
121
    /**
122
     * Getter for extracting the advice closure from Interceptor
123
     */
124 2
    public function getRawAdvice(): Closure
125
    {
126 2
        return $this->adviceMethod;
127
    }
128
129
    /**
130
     * Serializes an interceptor into string representation
131
     *
132
     * @return string the string representation of the object or null
133
     */
134 1
    public function serialize()
135
    {
136 1
        $vars = array_filter(get_object_vars($this));
137 1
        $vars['adviceMethod'] = static::serializeAdvice($this->adviceMethod);
138
139 1
        return serialize($vars);
140
    }
141
142
    /**
143
     * Unserialize an interceptor from the string
144
     *
145
     * @param string $serialized The string representation of the object.
146
     * @return void
147
     */
148 1
    public function unserialize($serialized)
149
    {
150 1
        $vars = unserialize($serialized);
151 1
        $vars['adviceMethod'] = static::unserializeAdvice($vars['adviceMethod']);
152 1
        foreach ($vars as $key => $value) {
153 1
            $this->$key = $value;
154
        }
155 1
    }
156
}
157