/**
* Builds proxy class code which weaves advices into the respective target classes.
*
* The object configurations provided by the Compiler are searched for possible aspect
* annotations. If an aspect class is found, the pointcut expressions are parsed and
* a new aspect with one or more advisors is added to the aspect registry of the AOP framework.
* Finally all advices are woven into their target classes by generating proxy classes.
*
* In general, the command neos.flow:core:compile is responsible for compilation
* and calls this method to do so.
*
* In order to distinguish between an emerged / changed possible target class and
* a class which has been matched previously but just didn't have to be proxied,
* the latter are kept track of by an "unproxiedClass-*" cache entry.
*
* @return void
*/
public function build()
{
$allAvailableClassNamesByPackage = $this->objectManager->getRegisteredClassNames();
$possibleTargetClassNames = $this->getProxyableClasses($allAvailableClassNamesByPackage);
$actualAspectClassNames = $this->reflectionService->getClassNamesByAnnotation(Flow\Aspect::class);
sort($possibleTargetClassNames);
sort($actualAspectClassNames);
$this->aspectContainers = $this->buildAspectContainers($actualAspectClassNames);
$rebuildEverything = false;
if ($this->objectConfigurationCache->has('allAspectClassesUpToDate') === false) {
$rebuildEverything = true;
$this->systemLogger->log('Aspects have been modified, therefore rebuilding all target classes.', LOG_INFO);
$this->objectConfigurationCache->set('allAspectClassesUpToDate', true);
}
$possibleTargetClassNameIndex = new ClassNameIndex();
$possibleTargetClassNameIndex->setClassNames($possibleTargetClassNames);
$targetClassNameCandidates = new ClassNameIndex();
foreach ($this->aspectContainers as $aspectContainer) {
$targetClassNameCandidates->applyUnion($aspectContainer->reduceTargetClassNames($possibleTargetClassNameIndex));
}
$targetClassNameCandidates->sort();
$treatedSubClasses = new ClassNameIndex();
foreach ($targetClassNameCandidates->getClassNames() as $targetClassName) {
$isUnproxied = $this->objectConfigurationCache->has('unproxiedClass-' . str_replace('\\', '_', $targetClassName));
$hasCacheEntry = $this->compiler->hasCacheEntryForClass($targetClassName) || $isUnproxied;
if ($rebuildEverything === true || $hasCacheEntry === false) {
$proxyBuildResult = $this->buildProxyClass($targetClassName, $this->aspectContainers);
if ($proxyBuildResult === false) {
// In case the proxy was not build because there was nothing adviced,
// it might be an advice in the parent and so we need to try to treat this class.
$treatedSubClasses = $this->addBuildMethodsAndAdvicesCodeToClass($targetClassName, $treatedSubClasses);
}
$treatedSubClasses = $this->proxySubClassesOfClassToEnsureAdvices($targetClassName, $targetClassNameCandidates, $treatedSubClasses);
if ($proxyBuildResult !== false) {
if ($isUnproxied) {
$this->objectConfigurationCache->remove('unproxiedClass-' . str_replace('\\', '_', $targetClassName));
}
$this->systemLogger->log(sprintf('Built AOP proxy for class "%s".', $targetClassName), LOG_DEBUG);
} else {
$this->objectConfigurationCache->set('unproxiedClass-' . str_replace('\\', '_', $targetClassName), true);
}
}
}
}