Skip to content
This repository has been archived by the owner on Feb 18, 2020. It is now read-only.

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
matej21 committed Aug 13, 2014
0 parents commit c7fc888
Show file tree
Hide file tree
Showing 18 changed files with 756 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
vendor
tests/**/*.actual
tests/**/*.expected
tests/**/coverage.*
composer.lock
tests/tmp
!.gitkeep
29 changes: 29 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "librette/setup",
"keywords": ["nette", "librette", "di"],
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
"authors": [
{
"name": "David Matějka",
"homepage": "http://www.matej21.cz"
}
],
"require": {
"nette/di": "~2.2@dev",
"nette/utils": "~2.2@dev",
"nette/reflection": "~2.2@dev"
},
"require-dev": {
"nette/bootstrap": "~2.2@dev",
"nette/neon": "~2.2@dev",
"nette/php-generator": "~2.2@dev",
"nette/tester": "@dev",
"tracy/tracy": "~2.2@dev",
"mockery/mockery": "@stable"
},
"autoload": {
"psr-4": {
"Librette\\Setup\\": "src/"
}
}
}
18 changes: 18 additions & 0 deletions src/IProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
namespace Librette\Setup;

use Nette\DI\ServiceDefinition;

/**
* @author David Matejka
*/
interface IProcessor
{

/**
* @param ServiceDefinition
* @param mixed
* @return void
*/
public function process(ServiceDefinition $definition, $args);
}
22 changes: 22 additions & 0 deletions src/Processors/InjectProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php
namespace Librette\Setup\Processors;

use Librette\Setup\IProcessor;
use Nette\DI\ServiceDefinition;
use Nette\Object;

/**
* @author David Matejka
*/
class InjectProcessor extends Object implements IProcessor
{


public function process(ServiceDefinition $definition, $args)
{
if ($args === TRUE) {
$definition->setInject(TRUE);
}
}

}
36 changes: 36 additions & 0 deletions src/Processors/SetupProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
namespace Librette\Setup\Processors;

use Librette\Setup\IProcessor;
use Nette\DI\ServiceDefinition;
use Nette\DI\Statement;
use Nette\Object;
use Nette\Utils\Validators;

/**
* @author David Matejka
*/
class SetupProcessor extends Object implements IProcessor
{


public function process(ServiceDefinition $definition, $methods)
{
$currentSetupMethods = array_map(function (Statement $statement) {
return $statement->getEntity();
}, $definition->getSetup());
Validators::assert($methods, 'array');
foreach ($methods as $setup) {
$method = $setup;
$args = array();
if ($method instanceof Statement) {
$args = $method->arguments;
$method = $method->getEntity();
}
if (!in_array($method, $currentSetupMethods)) {
$definition->addSetup($method, $args);
}
}
}

}
33 changes: 33 additions & 0 deletions src/Processors/TagsProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
namespace Librette\Setup\Processors;

use Librette\Setup\IProcessor;
use Nette\DI\ServiceDefinition;
use Nette\Object;

/**
* @author David Matejka
*/
class TagsProcessor extends Object implements IProcessor
{


public function process(ServiceDefinition $definition, $tags)
{
if (is_string($tags)) {
$tags = array($tags);
}

$currentTags = $definition->getTags();
foreach ((array) $tags as $tag => $attrs) {
if (is_int($tag) && is_string($attrs)) {
$tag = $attrs;
$attrs = TRUE;
}
if (!isset($currentTags[$tag])) {
$definition->addTag($tag, $attrs);
}
}
}

}
125 changes: 125 additions & 0 deletions src/SetupExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php
namespace Librette\Setup;

use Nette\DI\CompilerExtension;
use Nette\DI\ServiceDefinition;
use Nette\Reflection;


/**
* @author David Matejka
*/
class SetupExtension extends CompilerExtension
{

public $defaults = array(
'processors' => array(
'setup' => 'Librette\Setup\Processors\SetupProcessor',
'inject' => 'Librette\Setup\Processors\InjectProcessor',
'tags' => 'Librette\Setup\Processors\TagsProcessor',
),
'targets' => array(),
);


public function beforeCompile()
{
$builder = $this->getContainerBuilder();
$config = $this->getConfig($this->defaults);
$processors = $config['processors'];
/** @var IProcessor[] $initializedProcessors */
$initializedProcessors = array();
$targets = $this->getTargets($config);
/** @var ServiceDefinition $definition */
foreach ($builder->getDefinitions() as $definition) {
if (!($class = $this->detectClass($definition))) {
continue;
}
$types = self::getAllClassTypes($class);
foreach ($targets as $target) {
if (count(array_intersect($target['type'], $types)) === 0) {
continue;
}
unset($target['type']);
foreach ($target as $processor => $args) {
if (is_int($processor)) {
$processor = $args;
$args = TRUE;
}
if (!isset($processors[$processor])) {
throw new \RuntimeException("Setup processor \"{$processor}\" not exists.");
}
if (!isset($initializedProcessors[$processor])) {
$initializedProcessors[$processor] = new $processors[$processor]($builder);
}
$initializedProcessors[$processor]->process($definition, $args);
}
}
}

}


/**
* @param ServiceDefinition
* @return string|null
*/
protected static function detectClass(ServiceDefinition $def)
{
if ($def->getClass()) {
return $def->getClass();
} elseif ($interface = $def->getImplement()) {
$rc = Reflection\ClassType::from($interface);
$method = $rc->hasMethod('create') ? 'create' : ($rc->hasMethod('get') ? 'get' : NULL);
if ($method === NULL) {
return NULL;
}
if (!($returnType = $rc->getMethod($method)->getAnnotation('return'))) {
return NULL;
}

return Reflection\AnnotationsParser::expandClassName(preg_replace('#[|\s].*#', '', $returnType), $rc);
}

return NULL;
}


/**
* @param string
* @return array
*/
protected static function getAllClassTypes($class)
{
$rc = Reflection\ClassType::from($class);
$classTypes = array_merge(array($class), class_parents($class), class_implements($class));
if(PHP_VERSION_ID >= 50400) {
do {
$classTypes = array_merge($classTypes, $rc->getTraitNames());
} while ($rc = $rc->getParentClass());
}

return array_map(function ($val) {
return ltrim(strtolower($val), '\\');
}, $classTypes);
}


/**
* @param array
* @return array
*/
protected function getTargets($config)
{
$targets = $config['targets'];
unset($config['processors'], $config['targets']);
$targets = array_merge($targets, $config);
foreach ($targets as &$target) {
$target['type'] = array_map(function ($val) {
return ltrim(strtolower($val), '\\');
}, (array) $target['type']);
}

return $targets;
}
}
36 changes: 36 additions & 0 deletions tests/LibretteTests/Setup/CompilerExtensionTestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
namespace LibretteTests\Setup;

use Librette;
use Nette;
use Tester\TestCase;

require __DIR__ . '/mocks.php';


abstract class CompilerExtensionTestCase extends TestCase
{


protected function createExtension($targets)
{
$config = array(
'processors' => array(
'test' => 'LibretteTests\Setup\TestProcessor',
),
'targets' => $targets,
);
$compiler = \Mockery::mock('Nette\DI\Compiler');
$builder = new Nette\DI\ContainerBuilder();
$compiler->shouldReceive('getContainerBuilder')->andReturn($builder);
$compiler->shouldReceive('getConfig')->andReturn(array('setup' => $config));
$builder->addDefinition('foo')->setClass('LibretteTests\Setup\Foo');
$builder->addDefinition('bar')->setClass('LibretteTests\Setup\Bar');
$extension = new Librette\Setup\SetupExtension();
$extension->setCompiler($compiler, 'setup');

return $extension;

}

}
41 changes: 41 additions & 0 deletions tests/LibretteTests/Setup/ConfiguratorTestCase.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
namespace LibretteTests\Setup;

use Librette;
use Nette;
use Tester;

require_once __DIR__ . '/../bootstrap.php';
require_once __DIR__ . '/mocks.php';


/**
* @author David Matějka
*/
class ConfiguratorTestCase extends Tester\TestCase
{

public function setUp()
{
}


public function testConfigurator()
{
$configurator = new Nette\Configurator();
$configurator->defaultExtensions = array_intersect_key($configurator->defaultExtensions, array('extensions' => TRUE));
$configurator->autowireExcludedClasses = array();
$configurator->setTempDirectory(TEMP_DIR);
$configurator->addConfig(__DIR__ . '/config/basic.neon');

/** @var Nette\DI\Container $container */
$container = $configurator->createContainer();
/** @var Bar $bar */
$bar = $container->getService('bar');
Tester\Assert::same($container->getService('foo'), $bar->foo);
Tester\Assert::same($container->getService('lorem'), $bar->lorem);
}
}


\run(new ConfiguratorTestCase());
38 changes: 38 additions & 0 deletions tests/LibretteTests/Setup/InjectProcessorTestCase.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
namespace LibretteTests\Setup;

use Librette;
use Nette;
use Tester;

require_once __DIR__ . '/../bootstrap.php';


/**
* @author David Matějka
*/
class InjectProcessorTestCase extends CompilerExtensionTestCase
{

public function setUp()
{
}


public function testInject()
{
$extension = $this->createExtension(array(
array(
'type' => 'LibretteTests\Setup\Bar',
'inject' => TRUE,
)
));
$builder = $extension->getContainerBuilder();
$extension->beforeCompile();
Tester\Assert::false($builder->getDefinition('foo')->getInject());
Tester\Assert::true($builder->getDefinition('bar')->getInject());
}
}


\run(new InjectProcessorTestCase());
Loading

0 comments on commit c7fc888

Please sign in to comment.