Skip to content

Commit

Permalink
Merge pull request #5 from moufmouf/0.3
Browse files Browse the repository at this point in the history
Getting ready for 0.3
  • Loading branch information
moufmouf committed Nov 30, 2015
2 parents ecd01e5 + 5c49979 commit 43266a2
Show file tree
Hide file tree
Showing 41 changed files with 1,012 additions and 342 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
composer.lock
composer.phar
/vendor/
/build/
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ language: php

php:
- 5.5
- 5.6
- 7.0

before_script:
- wget http://getcomposer.org/composer.phar
Expand Down
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# YACO - Yet another compiler

YACO (Yet Another COmpiler) is a PHP tool that generates a PHP container based on entry definitions.
Entry definitions must be compatible with interfaces defined in [*compiler-interop*](https://github.com/container-interop/compiler-interop/)
It is fully compatible with entry definitions from [*definition-interop*](https://github.com/container-interop/definition-interop/).

## Installation

Expand All @@ -25,7 +25,7 @@ between minor versions.
## Usage

This package contains a `Compiler` class. The goal of this class is to take a number of "entry definitions"
(as defined in [*compiler-interop*](https://github.com/container-interop/compiler-interop/)) and to transform those
(as defined in [*definition-interop*](https://github.com/container-interop/definition-interop/)) and to transform those
into a PHP class that implements the [`ContainerInterface`](https://github.com/container-interop/container-interop/)

```php
Expand All @@ -36,10 +36,25 @@ $compiler = new Compiler();
// ...

foreach ($definitions as $definition) {
/* @var $definition TheCodingMachine\Yaco\Definition\DumpableInterface */
/* @var $definition Interop\Container\Definition\DefinitionInterface */
$compiler->addDefinition($definition);
}

// Let's dump the code of the My\Container class.
file_put_contents("Container.php", $compiler->compile("My\\Container"));
```

You can also directly register a **definition provider** using the *register* method:

```php
use TheCodingMachine\Yaco\Compiler;

$compiler = new Compiler();

// ...

$compiler->register($definitionProvider);

// Let's dump the code of the My\Container class.
file_put_contents("Container.php", $compiler->compile("My\\Container"));
```
14 changes: 5 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
{
"repositories": [
{
"type": "vcs",
"url": "git@github.com:container-interop/definition-interop.git"
}
],
"name": "thecodingmachine/yaco",
"type": "library",
"description": "YACO (Yet Another COmpiler) is a PHP tool that generates a PHP container based on entry definitions.",
Expand All @@ -20,14 +14,16 @@
}
},
"require": {
"container-interop/definition-interop": "dev-master",
"container-interop/definition-interop": "~0.1.0",
"container-interop/container-interop": "^1.0",
"jeremeamia/superclosure": "^2.0",
"mouf/picotainer": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "~5.0",
"satooshi/php-coveralls": "dev-master"
"phpunit/phpunit": "~4.5",
"mnapoli/assembly": "~0.1.0",
"satooshi/php-coveralls": "dev-master",
"container-interop/definition-interop-compiler-test-suite": "~0.1.0"
},
"minimum-stability": "dev",
"prefer-stable": true
Expand Down
100 changes: 82 additions & 18 deletions src/Compiler.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<?php

namespace TheCodingMachine\Yaco;

use Interop\Container\Definition\DefinitionInterface;
use Interop\Container\Definition\DefinitionProviderInterface;
use TheCodingMachine\Yaco\Definition\DumpableInterface;
use TheCodingMachine\Yaco\Definition\InlineEntryInterface;

Expand All @@ -8,25 +12,75 @@
*/
class Compiler
{
/**
* @var DefinitionInterface[]
*/
private $definitions = [];

/**
* @var DumpableInterface[]
*/
private $definitions = [];
private $dumpableDefinitions = [];

/**
* The object in charge of converting container-interop definitions to our internal standard.
*
* @var DefinitionConverterInterface
*/
private $converter;

/**
* @param DefinitionConverterInterface $converter The object in charge of converting container-interop definitions to our internal standard.
*/
public function __construct(DefinitionConverterInterface $converter = null)
{
if ($converter === null) {
$converter = new DefinitionConverter();
}
$this->converter = $converter;
}

/**
* @param DumpableInterface $definition
* Adds a definition to the list of definitions managed by this compiler.
*
* @param DefinitionInterface $definition
*/
public function addDefinition(DumpableInterface $definition) {
public function addDefinition(DefinitionInterface $definition)
{
$this->definitions[$definition->getIdentifier()] = $definition;
unset($this->dumpableDefinitions[$definition->getIdentifier()]);
}

/**
* Registers a new definition provider.
*
* @param DefinitionProviderInterface $definitionProvider
*/
public function register(DefinitionProviderInterface $definitionProvider) {
foreach ($definitionProvider->getDefinitions() as $definition) {
$this->addDefinition($definition);
}
}

/**
* Adds a dumpable definition to the list of definitions managed by this compiler.
* Note: a "dumpable" definition is a definition represented in Yaco internal format.
*
* @param DumpableInterface $dumpableDefinition
*/
public function addDumpableDefinition(DumpableInterface $dumpableDefinition)
{
$this->dumpableDefinitions[$dumpableDefinition->getIdentifier()] = $dumpableDefinition;
unset($this->definitions[$dumpableDefinition->getIdentifier()]);
}

/**
* @param string $className
*
* @return string
*/
public function compile($className) {

public function compile($className)
{
$classCode = <<<EOF
<?php
%s
Expand All @@ -47,47 +101,57 @@ public function __construct(ContainerInterface \$delegateLookupContainer = null)

list($shortClassName, $namespaceLine) = $this->splitFQCN($className);

$closuresCode = "";
$parametersCode = "";
$closuresCode = '';
$parametersCode = '';

// Let's merge dumpable definitions with standard definitions.
$convertedDefinitions = array_map([$this->converter, 'convert'], $this->definitions);
$allDefinitions = $convertedDefinitions + $this->dumpableDefinitions;

foreach ($this->definitions as $identifier => $definition) {
foreach ($allDefinitions as $identifier => $definition) {
$inlineEntry = $definition->toPhpCode('$container', ['$container']);

if ($inlineEntry->isLazilyEvaluated()) {
$closuresCode .= " ".var_export($identifier, true)." => ".$this->getClosureCode($inlineEntry).",\n";
$closuresCode .= ' '.var_export($identifier, true).' => '.$this->getClosureCode($inlineEntry).",\n";
} else {
$parametersCode .= " ".var_export($identifier, true)." => ".$this->getParametersCode($inlineEntry).",\n";
$parametersCode .= ' '.var_export($identifier, true).' => '.$this->getParametersCode($inlineEntry).",\n";
}
}

return sprintf($classCode, $namespaceLine, $shortClassName, $closuresCode, $parametersCode);
}

private function splitFQCN($className) {
private function splitFQCN($className)
{
$pos = strrpos($className, '\\');
if ($pos !== false) {
$shortClassName = substr($className, $pos+1);
$namespaceLine = "namespace ".substr($className, 0, $pos).";";
$shortClassName = substr($className, $pos + 1);
$namespaceLine = 'namespace '.substr($className, 0, $pos).';';
} else {
$shortClassName = $className;
$namespaceLine = "";
$namespaceLine = '';
}

return [
$shortClassName,
$namespaceLine
$namespaceLine,
];
}

private function getParametersCode(InlineEntryInterface $inlineEntry) {
private function getParametersCode(InlineEntryInterface $inlineEntry)
{
if (!empty($inlineEntry->getStatements())) {
throw new CompilerException('An entry that contains parameters (not lazily loaded) cannot return statements.');
}

return $inlineEntry->getExpression();
}

private function getClosureCode(InlineEntryInterface $inlineEntry) {
private function getClosureCode(InlineEntryInterface $inlineEntry)
{
$code = $inlineEntry->getStatements();
$code .= "return ".$inlineEntry->getExpression().";\n";
$code .= 'return '.$inlineEntry->getExpression().";\n";

return sprintf("function(\$container) {\n%s}", $code);
}
}
5 changes: 1 addition & 4 deletions src/CompilerException.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
<?php


namespace TheCodingMachine\Yaco;


class CompilerException extends \Exception
{

}
}
7 changes: 4 additions & 3 deletions src/Definition/ActionInterface.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
<?php
namespace TheCodingMachine\Yaco\Definition;

namespace TheCodingMachine\Yaco\Definition;

/**
* Classes implementing ActionInterface represent a line of PHP code that is an action performed on an object.
* This can be a method call or a public property assignment.
*/
interface ActionInterface
{

/**
* Generates PHP code for the line.
*
* @param string $variableName
* @param string $containerVariable
* @param array $usedVariables
* @param array $usedVariables
*
* @return InlineEntryInterface
*/
public function toPhpCode($variableName, $containerVariable, array $usedVariables);
Expand Down
9 changes: 6 additions & 3 deletions src/Definition/AliasDefinition.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<?php

namespace TheCodingMachine\Yaco\Definition;

/**
* This class represents an alias to another entry in the container.
*/
class AliasDefinition implements DumpableInterface
{

/**
* The identifier of the entry in the container.
*
Expand All @@ -25,7 +25,7 @@ class AliasDefinition implements DumpableInterface
* Constructs an instance definition.
*
* @param string|null $identifier The identifier of the entry in the container. Can be null if the entry is anonymous (declared inline in other instances)
* @param string $alias The identifier of the entry we are aliasing.
* @param string $alias The identifier of the entry we are aliasing.
*/
public function __construct($identifier, $alias)
{
Expand All @@ -35,6 +35,7 @@ public function __construct($identifier, $alias)

/**
* Returns the identifier of the instance.
*
* @return string
*/
public function getIdentifier()
Expand All @@ -44,6 +45,7 @@ public function getIdentifier()

/**
* The identifier of the entry we are aliasing.
*
* @return mixed
*/
public function getAlias()
Expand All @@ -56,7 +58,8 @@ public function getAlias()
* the container entry.
*
* @param string $containerVariable The name of the variable that allows access to the container instance. For instance: "$container", or "$this->container"
* @param array $usedVariables An array of variables that are already used and that should not be used when generating this code.
* @param array $usedVariables An array of variables that are already used and that should not be used when generating this code.
*
* @return InlineEntryInterface
*/
public function toPhpCode($containerVariable, array $usedVariables = array())
Expand Down
13 changes: 9 additions & 4 deletions src/Definition/ClosureDefinition.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace TheCodingMachine\Yaco\Definition;

use SuperClosure\Analyzer\TokenAnalyzer;
Expand All @@ -9,7 +10,6 @@
*/
class ClosureDefinition implements DumpableInterface
{

/**
* The identifier of the instance in the container.
*
Expand All @@ -28,7 +28,7 @@ class ClosureDefinition implements DumpableInterface
* Constructs an instance definition.
*
* @param string|null $identifier The identifier of the entry in the container. Can be null if the entry is anonymous (declared inline in other instances)
* @param \Closure $closure The closure. It should not contain context (i.e. no "use" keyword in the closure definition). It should accept one compulsory parameter: the container.
* @param \Closure $closure The closure. It should not contain context (i.e. no "use" keyword in the closure definition). It should accept one compulsory parameter: the container.
*/
public function __construct($identifier, \Closure $closure)
{
Expand All @@ -38,6 +38,7 @@ public function __construct($identifier, \Closure $closure)

/**
* Returns the identifier of the instance.
*
* @return string
*/
public function getIdentifier()
Expand All @@ -47,6 +48,7 @@ public function getIdentifier()

/**
* Returns the closure of the parameter.
*
* @return mixed
*/
public function getClosure()
Expand All @@ -59,8 +61,10 @@ public function getClosure()
* the container entry.
*
* @param string $containerVariable The name of the variable that allows access to the container instance. For instance: "$container", or "$this->container"
* @param array $usedVariables An array of variables that are already used and that should not be used when generating this code.
* @param array $usedVariables An array of variables that are already used and that should not be used when generating this code.
*
* @return InlineEntryInterface
*
* @throws DefinitionException
*/
public function toPhpCode($containerVariable, array $usedVariables = array())
Expand All @@ -78,7 +82,8 @@ public function toPhpCode($containerVariable, array $usedVariables = array())
$code = $analysis['code'];
$variableName = VariableUtils::getNextAvailableVariableName("\$closure", $usedVariables);
$usedVariables[] = $variableName;
$assignClosure = sprintf("%s = %s;", $variableName, $code);
$assignClosure = sprintf('%s = %s;', $variableName, $code);

return new InlineEntry($variableName.'('.$containerVariable.')', $assignClosure, $usedVariables);
}
}
Loading

0 comments on commit 43266a2

Please sign in to comment.