Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introducing aliases for Beans #55

Merged
merged 2 commits into from
Aug 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 19 additions & 53 deletions src/bitExpert/Disco/AnnotationBeanFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
declare(strict_types = 1);

namespace bitExpert\Disco;

use bitExpert\Disco\Proxy\Configuration\AliasContainerInterface;
use bitExpert\Disco\Proxy\Configuration\ConfigurationFactory;
use Exception;

/**
* {@link \bitExpert\Disco\BeanFactory} implementation.
Expand All @@ -35,7 +35,7 @@ class AnnotationBeanFactory implements BeanFactory
*/
protected $config;
/**
* @var object
* @var AliasContainerInterface
*/
protected $beanStore;

Expand Down Expand Up @@ -64,19 +64,22 @@ public function get($id)
{
$instance = null;

$id = $this->normalizeBeanId($id);
if ($this->beanIdExists($id)) {
try {
$instance = call_user_func([$this->beanStore, $id]);
} catch (\Throwable $e) {
$message = sprintf(
'Exception occured while instanciating "%s": %s',
$id,
$e->getMessage()
);

throw new BeanException($message, 0, $e);
try {
if (is_callable([$this->beanStore, $id])) {
$instance = $this->beanStore->$id();
}

if ($this->beanStore->hasAlias($id)) {
$instance = $this->beanStore->getAlias($id);
}
} catch (\Throwable $e) {
$message = sprintf(
'Exception occured while instanciating "%s": %s',
$id,
$e->getMessage()
);

throw new BeanException($message, 0, $e);
}

if (null === $instance) {
Expand All @@ -91,8 +94,7 @@ public function get($id)
*/
public function has($id)
{
$id = $this->normalizeBeanId($id);
return $this->beanIdExists($id);
return is_callable([$this->beanStore, $id]) || $this->beanStore->hasAlias($id);
}

/**
Expand Down Expand Up @@ -124,40 +126,4 @@ protected function initBeanStore($configClassName, array $parameters, BeanFactor
$configFactory = new ConfigurationFactory($config);
return $configFactory->createInstance($configClassName, $parameters);
}

/**
* Helper method to "normalize" bean identifiers. Since the bean identifier is basically a method name
* of the config class we can only support a subset of characters, namely alphabetic characters, digits
* and underscore.
*
* @param string $id
* @return string
*/
protected function normalizeBeanId($id)
{
// filter out all invalid characters
$id = preg_replace('#[^a-zA-Z0-9_]#', '', $id);
// prepend underscore when first character is neither an alphabetic character nor a underscore
if (!preg_match('#^[a-zA-Z_]#', $id)) {
$id = '_' . $id;
}

return $id;
}

/**
* Returns true if the container can return an entry for the given identifier.
* Returns false otherwise. Expects $id to be normalized!
*
* @param string $id Identifier of the entry to look for.
* @return boolean
*/
protected function beanIdExists($id)
{
if (empty($id) or !is_string($id)) {
return false;
}

return is_callable([$this->beanStore, $id]);
}
}
17 changes: 17 additions & 0 deletions src/bitExpert/Disco/Annotations/Bean.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

namespace bitExpert\Disco\Annotations;

use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
use Doctrine\Common\Annotations\AnnotationException;

/**
Expand All @@ -21,6 +23,7 @@
* @Attribute("scope", type = "string"),
* @Attribute("singleton", type = "bool"),
* @Attribute("lazy", type = "bool"),
* @Attribute("alias", type = "string"),
* })
*/
class Bean
Expand Down Expand Up @@ -52,6 +55,7 @@ public function __construct(array $attributes = [])
$this->scope = self::SCOPE_REQUEST;
$this->singleton = true;
$this->lazy = false;
$this->alias = '';

if (isset($attributes['value'])) {
if (isset($attributes['value']['scope']) && (strtolower($attributes['value']['scope']) === 'session')) {
Expand All @@ -65,6 +69,10 @@ public function __construct(array $attributes = [])
if (isset($attributes['value']['lazy'])) {
$this->lazy = $this->parseBooleanValue($attributes['value']['lazy']);
}

if (isset($attributes['value']['alias'])) {
$this->alias = $attributes['value']['alias'];
}
}
}

Expand Down Expand Up @@ -108,6 +116,15 @@ public function isLazy() : bool
return $this->lazy;
}

/**
* Returns the alias for the bean instance. Returns an empty string when no alias was set.
* @return string
*/
public function getAlias(): string
{
return $this->alias;
}

/**
* Helper function to cast a string value to a boolean representation.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

/*
* This file is part of the Disco package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace bitExpert\Disco\Proxy\Configuration;

use bitExpert\Disco\BeanException;
use bitExpert\Disco\BeanNotFoundException;

/**
* Interface similar to {@link \Interop\Container\ContainerInterface}. The interface is needed
* to be able to retrieve aliased beans from the generated configuration class.
*/
interface AliasContainerInterface
{
/**
* Finds an entry of the container by the given alias and returns it.
*
* @param string $alias Alias of the entry to look for.
* @return mixed
* @throws BeanNotFoundException No entry was found for this alias.
* @throws BeanException Error while retrieving the entry.
*/
public function getAlias(string $alias);

/**
* Returns true if the container can return an entry for the given alias.
* Returns false otherwise.
*
* @param string $alias Identifier of the entry to look for
* @return boolean
*/
public function hasAlias(string $alias) : bool;
}
33 changes: 33 additions & 0 deletions src/bitExpert/Disco/Proxy/Configuration/AliasesProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of the Disco package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);

namespace bitExpert\Disco\Proxy\Configuration;

use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use Zend\Code\Generator\PropertyGenerator;

/**
* Private property to store alias lookups for the bean instances.
*/
class AliasesProperty extends PropertyGenerator
{
/**
* Creates a new {@link \bitExpert\Disco\Proxy\Configuration\AliasesProperty}.
*/
public function __construct()
{
parent::__construct(UniqueIdentifierGenerator::getIdentifier('aliases'));

$this->setVisibility(self::VISIBILITY_PRIVATE);
$this->setDocBlock('@var array contains a list of aliases and their bean references');
}
}
25 changes: 25 additions & 0 deletions src/bitExpert/Disco/Proxy/Configuration/ConfigurationGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
use bitExpert\Disco\Annotations\Parameters;
use bitExpert\Disco\Proxy\Configuration\MethodGenerator\BeanMethod;
use bitExpert\Disco\Proxy\Configuration\MethodGenerator\Constructor;
use bitExpert\Disco\Proxy\Configuration\MethodGenerator\GetAlias;
use bitExpert\Disco\Proxy\Configuration\MethodGenerator\GetParameter;
use bitExpert\Disco\Proxy\Configuration\MethodGenerator\HasAlias;
use bitExpert\Disco\Proxy\Configuration\MethodGenerator\MagicSleep;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
Expand Down Expand Up @@ -78,6 +80,7 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe
$sessionBeansProperty = new SessionBeansProperty();
$postProcessorsProperty = new BeanPostProcessorsProperty();
$parameterValuesProperty = new ParameterValuesProperty();
$aliasesProperty = new AliasesProperty();
$getParameterMethod = new GetParameter($originalClass, $parameterValuesProperty);

try {
Expand All @@ -96,12 +99,15 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe
}

$classGenerator->setExtendedClass($originalClass->getName());
$classGenerator->setImplementedInterfaces([AliasContainerInterface::class]);
$classGenerator->addPropertyFromGenerator($forceLazyInitProperty);
$classGenerator->addPropertyFromGenerator($sessionBeansProperty);
$classGenerator->addPropertyFromGenerator($postProcessorsProperty);
$classGenerator->addPropertyFromGenerator($parameterValuesProperty);
$classGenerator->addPropertyFromGenerator($aliasesProperty);

$postProcessorMethods = [];
$aliases = [];
$methods = $originalClass->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED);
foreach ($methods as $method) {
if (null !== $this->reader->getMethodAnnotation($method, BeanPostProcessor::class)) {
Expand All @@ -121,6 +127,11 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe
);
}

// if alias is defined append it to the aliases list
if ($beanAnnotation->getAlias() !== '' && !isset($aliases[$beanAnnotation->getAlias()])) {
$aliases[$beanAnnotation->getAlias()] = $method->getName();
}

/* @var \bitExpert\Disco\Annotations\Parameters $parametersAnnotation */
$parametersAnnotation = $this->reader->getMethodAnnotation($method, Parameters::class);
if (null === $parametersAnnotation) {
Expand Down Expand Up @@ -168,6 +179,8 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe
$classGenerator->addMethodFromGenerator($proxyMethod);
}

$aliasesProperty->setDefaultValue($aliases);

$classGenerator->addMethodFromGenerator(
new Constructor(
$originalClass,
Expand All @@ -183,5 +196,17 @@ public function generate(ReflectionClass $originalClass, ClassGenerator $classGe
$sessionBeansProperty
)
);
$classGenerator->addMethodFromGenerator(
new GetAlias(
$originalClass,
$aliasesProperty
)
);
$classGenerator->addMethodFromGenerator(
new HasAlias(
$originalClass,
$aliasesProperty
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

/*
* This file is part of the Disco package.
*
* (c) bitExpert AG
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types = 1);

namespace bitExpert\Disco\Proxy\Configuration\MethodGenerator;

use bitExpert\Disco\BeanNotFoundException;
use bitExpert\Disco\Proxy\Configuration\AliasesProperty;
use ProxyManager\Generator\MethodGenerator;
use ReflectionClass;
use Zend\Code\Generator\ParameterGenerator;

/**
* `getAlias` method for the generated config proxy class.
*/
class GetAlias extends MethodGenerator
{
/**
* Creates a new {@link \bitExpert\Disco\Proxy\Configuration\MethodGenerator\GetAlias}.
*
* @param ReflectionClass $originalClass
* @param AliasesProperty $aliasesProperty
*/
public function __construct(ReflectionClass $originalClass, AliasesProperty $aliasesProperty)
{
parent::__construct('getAlias');

$aliasParameter = new ParameterGenerator('alias');
$aliasParameter->setType('string');

$body = 'if ($this->hasAlias($'.$aliasParameter->getName().')) {' . PHP_EOL;
$body .= ' $methodname = $this->' . $aliasesProperty->getName() . '[$'.$aliasParameter->getName().'];' .
PHP_EOL;
$body .= ' return $this->$methodname();' . PHP_EOL;
$body .= '}' . PHP_EOL . PHP_EOL;
$body .= 'throw new '.BeanNotFoundException::class.'(sprintf(\'Alias "%s" is not defined!\', $'.
$aliasParameter->getName().'));' . PHP_EOL;

$this->setParameter($aliasParameter);
$this->setVisibility(self::VISIBILITY_PUBLIC);
$this->setBody($body);
}
}
Loading