Skip to content

Commit

Permalink
FEATURE: Add Flow\InjectCache Annotation and Attribute
Browse files Browse the repository at this point in the history
The `#[Flow\InjectCache]` attribute resp. the`@Flow\InjectCache` annotation allows to assign a cache frontend of a configured cache directly into a property without having to configure the Objects.yaml.
  • Loading branch information
mficzel committed Nov 29, 2023
1 parent 5689036 commit 2a46c5f
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 0 deletions.
42 changes: 42 additions & 0 deletions Neos.Flow/Classes/Annotations/InjectCache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
namespace Neos\Flow\Annotations;

/*
* This file is part of the Neos.Flow package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;

/**
* Used to enable property injection for cache frontends.
*
* Flow will build Dependency Injection code for the property and try
* to inject the specified cache.
*
* @Annotation
* @NamedArgumentConstructor
* @Target("PROPERTY")
*/
#[\Attribute(\Attribute::TARGET_PROPERTY)]
final class InjectCache
{
/**
* Identifier for the Cache that will be injected.
*
* Example: Neos_Fusion_Content
*
* @var string
*/
public $identifier;

public function __construct(string $identifier)
{
$this->identifier = $identifier;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Annotations\Inject;
use Neos\Flow\Annotations\InjectCache;
use Neos\Flow\Annotations\InjectConfiguration;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\ObjectManagement\Exception as ObjectException;
Expand Down Expand Up @@ -617,6 +618,18 @@ protected function autowireProperties(array &$objectConfigurations)
}
$properties[$propertyName] = new ConfigurationProperty($propertyName, ['type' => $injectConfigurationAnnotation->type, 'path' => $configurationPath], ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION);
}

foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, InjectCache::class) as $propertyName) {
if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) {
throw new ObjectException(sprintf('The property "%s" in class "%s" must not be private when annotated for cache injection.', $propertyName, $className), 1416765599);
}
if (array_key_exists($propertyName, $properties)) {
continue;
}
/** @var InjectCache $injectCacheAnnotation */
$injectCacheAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, InjectCache::class);
$properties[$propertyName] = new ConfigurationProperty($propertyName, ['identifier' => $injectCacheAnnotation->identifier], ConfigurationProperty::PROPERTY_TYPES_CACHE);
}
$objectConfiguration->setProperties($properties);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class ConfigurationProperty
const PROPERTY_TYPES_STRAIGHTVALUE = 0;
const PROPERTY_TYPES_OBJECT = 1;
const PROPERTY_TYPES_CONFIGURATION = 2;
const PROPERTY_TYPES_CACHE = 3;

/**
* @var string Name of the property
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cache\CacheManager;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Configuration\Exception\InvalidConfigurationTypeException;
use Neos\Flow\Log\Utility\LogEnvironment;
Expand Down Expand Up @@ -45,6 +46,7 @@ class ProxyClassBuilder
protected Compiler $compiler;
protected LoggerInterface $logger;
protected ConfigurationManager $configurationManager;
protected CacheManager $cacheManager;
protected CompileTimeObjectManager $objectManager;

/**
Expand All @@ -67,6 +69,11 @@ public function injectConfigurationManager(ConfigurationManager $configurationMa
$this->configurationManager = $configurationManager;
}

public function injectCacheManager(CacheManager $cacheManager): void
{
$this->cacheManager = $cacheManager;
}

#[Flow\Autowiring(false)]
public function injectLogger(LoggerInterface $logger): void
{
Expand Down Expand Up @@ -395,6 +402,13 @@ protected function buildPropertyInjectionCode(Configuration $objectConfiguration
}
$commands = array_merge($commands, $this->buildPropertyInjectionCodeByConfigurationTypeAndPath($objectConfiguration, $propertyName, $configurationType, $propertyValue['path']));
break;
case ConfigurationProperty::PROPERTY_TYPES_CACHE:
$cacheIdentifier = $propertyValue['identifier'];
if (!array_key_exists($cacheIdentifier, $this->cacheManager->getCacheConfigurations())) {
throw new UnknownObjectException('The cache injection specified for property "' . $propertyName . '" in the object configuration of object "' . $objectConfiguration->getObjectName() . '" refers to the unknown cache identifier "' . $cacheIdentifier . '".', 1701280300);
}
$commands = array_merge($commands, $this->buildPropertyInjectionCodeByCacheIdentifier($objectConfiguration, $propertyName, $cacheIdentifier));
break;
}
$injectedProperties[] = $propertyName;
}
Expand Down Expand Up @@ -513,6 +527,25 @@ public function buildPropertyInjectionCodeByConfigurationTypeAndPath(Configurati
return ['$this->' . $propertyName . ' = ' . $preparedSetterArgument . ';'];
}

/**
* Builds code which assigns the frontend of the specified cache into the given class property.
*
* @param Configuration $objectConfiguration Configuration of the object to inject into
* @param string $propertyName Name of the property to inject
* @param string $cacheIdentiier the configuration type of the injected property (one of the ConfigurationManager::CONFIGURATION_TYPE_* constants)
* @return array PHP code
*/
public function buildPropertyInjectionCodeByCacheIdentifier(Configuration $objectConfiguration, string $propertyName, string $cacheIdentiier): array
{
$className = $objectConfiguration->getClassName();
$preparedSetterArgument = '\Neos\Flow\Core\Bootstrap::$staticObjectManager->get(\Neos\Flow\Cache\CacheManager::class)->getCache(\'' . $cacheIdentiier . '\')';
$result = $this->buildSetterInjectionCode($className, $propertyName, $preparedSetterArgument);
if ($result !== null) {
return $result;
}
return ['$this->' . $propertyName . ' = ' . $preparedSetterArgument . ';'];
}

/**
* Builds code which injects a DependencyProxy instead of the actual dependency
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* source code.
*/

use Neos\Flow\Cache\CacheManager;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\ObjectManagement\Proxy\ProxyInterface;
use Neos\Flow\Tests\Functional\ObjectManagement\Fixtures\FinalClassWithDependencies;
Expand All @@ -28,12 +29,14 @@
class DependencyInjectionTest extends FunctionalTestCase
{
protected ConfigurationManager $configurationManager;
protected CacheManager $cacheManager;

protected function setUp(): void
{
parent::setUp();

$this->configurationManager = $this->objectManager->get(ConfigurationManager::class);
$this->cacheManager = $this->objectManager->get(CacheManager::class);
}

/**
Expand Down Expand Up @@ -295,6 +298,16 @@ public function injectionOfOtherConfigurationTypes(): void
self::assertSame($this->configurationManager->getConfiguration('Views'), $classWithInjectedConfiguration->getInjectedViewsConfiguration());
}

/**
* @test
*/
public function injectionOfCaches(): void
{
$classWithInjectedCache = new Fixtures\ClassWithInjectedCache();
self::assertSame($this->cacheManager->getCache('Flow_Monitor'), $classWithInjectedCache->getCacheInjectedViaAttribute());
self::assertSame($this->cacheManager->getCache('Flow_Monitor'), $classWithInjectedCache->getCacheInjectedViaAnnotation());
}

/**
* This test verifies the behaviour described in FLOW-175.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
namespace Neos\Flow\Tests\Functional\ObjectManagement\Fixtures;

/*
* This file is part of the Neos.Flow package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\Cache\Frontend\StringFrontend;
use Neos\Flow\Annotations as Flow;

/**
* A class for testing setting injection
*/
class ClassWithInjectedCache
{
/**
* @Flow\InjectCache(identifier="Flow_Monitor")
* @var StringFrontend
*/
protected $cacheByAnnotation;

#[Flow\InjectCache(identifier: 'Flow_Monitor')]
protected StringFrontend $cacheByAttribute;

public function getCacheInjectedViaAnnotation()
{
return $this->cacheByAnnotation;
}

public function getCacheInjectedViaAttribute()
{
return $this->cacheByAttribute;
}
}

0 comments on commit 2a46c5f

Please sign in to comment.