1 | <?php |
||
33 | class WeavingTransformer extends BaseSourceTransformer |
||
34 | { |
||
35 | |||
36 | /** |
||
37 | * Reflection broker instance |
||
38 | * |
||
39 | * @var Broker |
||
40 | */ |
||
41 | protected $broker; |
||
42 | |||
43 | /** |
||
44 | * @var AdviceMatcher |
||
45 | */ |
||
46 | protected $adviceMatcher; |
||
47 | |||
48 | /** |
||
49 | * @var CachePathManager |
||
50 | */ |
||
51 | private $cachePathManager; |
||
52 | |||
53 | /** |
||
54 | * Instance of aspect loader |
||
55 | * |
||
56 | * @var AspectLoader |
||
57 | */ |
||
58 | protected $aspectLoader; |
||
59 | |||
60 | /** |
||
61 | * Constructs a weaving transformer |
||
62 | * |
||
63 | * @param AspectKernel $kernel Instance of aspect kernel |
||
64 | * @param Broker $broker Instance of reflection broker to use |
||
65 | * @param AdviceMatcher $adviceMatcher Advice matcher for class |
||
66 | * @param CachePathManager $cachePathManager Cache manager |
||
67 | * @param AspectLoader $loader Loader for aspects |
||
68 | */ |
||
69 | 8 | public function __construct( |
|
83 | |||
84 | /** |
||
85 | * This method may transform the supplied source and return a new replacement for it |
||
86 | * |
||
87 | * @param StreamMetaData $metadata Metadata for source |
||
88 | * @return boolean Return false if transformation should be stopped |
||
89 | */ |
||
90 | 8 | public function transform(StreamMetaData $metadata) |
|
91 | { |
||
92 | 8 | $totalTransformations = 0; |
|
93 | |||
94 | 8 | $fileName = $metadata->uri; |
|
95 | |||
96 | try { |
||
97 | 8 | CleanableMemory::enterProcessing(); |
|
98 | 8 | $parsedSource = $this->broker->processString($metadata->source, $fileName, true); |
|
99 | } catch (FileProcessingException $e) { |
||
100 | CleanableMemory::leaveProcessing(); |
||
101 | |||
102 | return false; |
||
103 | } |
||
104 | |||
105 | // Check if we have some new aspects that weren't loaded yet |
||
106 | 8 | $unloadedAspects = $this->aspectLoader->getUnloadedAspects(); |
|
107 | 8 | if (!empty($unloadedAspects)) { |
|
108 | $this->loadAndRegisterAspects($unloadedAspects); |
||
109 | } |
||
110 | 8 | $advisors = $this->container->getByTag('advisor'); |
|
111 | |||
112 | /** @var $namespaces ParsedFileNamespace[] */ |
||
113 | 8 | $namespaces = $parsedSource->getNamespaces(); |
|
114 | 8 | $lineOffset = 0; |
|
115 | |||
116 | 8 | foreach ($namespaces as $namespace) { |
|
117 | |||
118 | /** @var $classes ParsedClass[] */ |
||
119 | 8 | $classes = $namespace->getClasses(); |
|
120 | 8 | foreach ($classes as $class) { |
|
121 | |||
122 | 7 | $parentClassNames = array_merge( |
|
123 | 7 | $class->getParentClassNameList(), |
|
124 | 7 | $class->getInterfaceNames(), |
|
125 | 7 | $class->getTraitNames() |
|
126 | ); |
||
127 | |||
128 | 7 | foreach ($parentClassNames as $parentClassName) { |
|
129 | 1 | class_exists($parentClassName); // trigger autoloading of class/interface/trait |
|
130 | } |
||
131 | |||
132 | // Skip interfaces and aspects |
||
133 | 7 | if ($class->isInterface() || in_array(Aspect::class, $class->getInterfaceNames())) { |
|
134 | 2 | continue; |
|
135 | } |
||
136 | 5 | $wasClassProcessed = $this->processSingleClass($advisors, $metadata, $class, $lineOffset); |
|
137 | 5 | $totalTransformations += (integer) $wasClassProcessed; |
|
138 | } |
||
139 | 8 | $wasFunctionsProcessed = $this->processFunctions($advisors, $metadata, $namespace); |
|
140 | 8 | $totalTransformations += (integer) $wasFunctionsProcessed; |
|
141 | } |
||
142 | |||
143 | 8 | CleanableMemory::leaveProcessing(); |
|
144 | |||
145 | // If we return false this will indicate no more transformation for following transformers |
||
146 | 8 | return $totalTransformations > 0; |
|
147 | } |
||
148 | |||
149 | /** |
||
150 | * Performs weaving of single class if needed |
||
151 | * |
||
152 | * @param array|Advisor[] $advisors |
||
153 | * @param StreamMetaData $metadata Source stream information |
||
154 | * @param ParsedClass $class Instance of class to analyze |
||
155 | * @param integer $lineOffset Current offset, will be updated to store the last position |
||
156 | * |
||
157 | * @return bool True if was class processed, false otherwise |
||
158 | */ |
||
159 | 5 | private function processSingleClass(array $advisors, StreamMetaData $metadata, ParsedClass $class, &$lineOffset) |
|
212 | |||
213 | /** |
||
214 | * Adjust definition of original class source to enable extending |
||
215 | * |
||
216 | * @param ParsedClass $class Instance of class reflection |
||
217 | * @param string $source Source code |
||
218 | * @param string $newParentName New name for the parent class |
||
219 | * |
||
220 | * @return string Replaced code for class |
||
221 | */ |
||
222 | 5 | private function adjustOriginalClass($class, $source, $newParentName) |
|
237 | |||
238 | /** |
||
239 | * Performs weaving of functions in the current namespace |
||
240 | * |
||
241 | * @param array|Advisor[] $advisors List of advisors |
||
242 | * @param StreamMetaData $metadata Source stream information |
||
243 | * @param ParsedFileNamespace $namespace Current namespace for file |
||
244 | * |
||
245 | * @return boolean True if functions were processed, false otherwise |
||
246 | */ |
||
247 | 8 | private function processFunctions(array $advisors, StreamMetaData $metadata, $namespace) |
|
276 | |||
277 | /** |
||
278 | * Save AOP proxy to the separate file anr returns the php source code for inclusion |
||
279 | * |
||
280 | * @param ParsedClass $class Original class reflection |
||
281 | * @param ClassProxy $child |
||
282 | * |
||
283 | * @return string |
||
284 | */ |
||
285 | 5 | private function saveProxyToCache($class, $child) |
|
319 | |||
320 | /** |
||
321 | * Utility method to load and register unloaded aspects |
||
322 | * |
||
323 | * @param array $unloadedAspects List of unloaded aspects |
||
324 | */ |
||
325 | private function loadAndRegisterAspects(array $unloadedAspects) |
||
331 | } |
||
332 |