Skip to content

Commit

Permalink
Merge pull request #3 from cycle/feature/remove_stubs
Browse files Browse the repository at this point in the history
Feature/remove stubs
  • Loading branch information
vvval committed Jun 3, 2019
2 parents 503081f + f225a3f commit 57dad75
Show file tree
Hide file tree
Showing 15 changed files with 137 additions and 87 deletions.
1 change: 0 additions & 1 deletion src/Declaration/Extractor/Methods.php
Expand Up @@ -22,7 +22,6 @@ final class Methods
'__toString',
'__invoke',
'__set_state',
// '__clone',
'__debuginfo',
];

Expand Down
4 changes: 2 additions & 2 deletions src/Factory.php
Expand Up @@ -13,7 +13,7 @@

final class Factory implements PromiseFactoryInterface, SingletonInterface
{
/** @var ProxyPrinter */
/** @var Printer */
private $printer;

/** @var MaterializerInterface */
Expand All @@ -32,7 +32,7 @@ final class Factory implements PromiseFactoryInterface, SingletonInterface
private $resolved = [];

public function __construct(
ProxyPrinter $printer,
Printer $printer,
MaterializerInterface $materializer,
Names $names,
Instantiator $instantiator,
Expand Down
69 changes: 46 additions & 23 deletions src/ProxyPrinter.php → src/Printer.php
Expand Up @@ -4,21 +4,19 @@
namespace Cycle\ORM\Promise;

use Cycle\ORM\ORMInterface;
use Cycle\ORM\Promise\Declaration\DeclarationInterface;
use Cycle\ORM\Promise\Declaration\Extractor;
use Cycle\ORM\Promise\Declaration\Structure;
use Cycle\ORM\Promise\Declaration;
use PhpParser\Lexer;
use PhpParser\Node;
use PhpParser\Parser;
use PhpParser\PrettyPrinter\Standard;
use PhpParser\PrettyPrinterAbstract;

class ProxyPrinter
class Printer
{
private const RESOLVER_PROPERTY = '__resolver';
private const UNSET_PROPERTIES = 'UNSET_PROPERTIES';
private const RESOLVE_METHOD = '__resolve';
private const INIT_METHOD = '__init';
private const RESOLVER_PROPERTY = '__resolver';
private const UNSET_PROPERTIES_CONST = 'UNSET_PROPERTIES';
private const RESOLVE_METHOD = '__resolve';
private const INIT_METHOD = '__init';

private const DEPENDENCIES = [
'orm' => ORMInterface::class,
Expand All @@ -27,18 +25,26 @@ class ProxyPrinter
];

private const USE_STMTS = [
PromiseInterface::class,
PromiseResolver::class,
PromiseException::class,
ORMInterface::class
];

private const PROMISE_METHODS = [
'__loaded' => 'bool',
'__role' => 'string',
'__scope' => 'array',
'__resolve' => null,
];

/** @var ConflictResolver */
private $resolver;

/** @var Traverser */
private $traverser;

/** @var Extractor */
/** @var Declaration\Extractor */
private $extractor;

/** @var Lexer */
Expand All @@ -53,7 +59,7 @@ class ProxyPrinter
/** @var Stubs */
private $stubs;

public function __construct(ConflictResolver $resolver, Traverser $traverser, Extractor $extractor, Stubs $stubs)
public function __construct(ConflictResolver $resolver, Traverser $traverser, Declaration\Extractor $extractor, Stubs $stubs)
{
$this->resolver = $resolver;
$this->traverser = $traverser;
Expand All @@ -76,36 +82,53 @@ public function __construct(ConflictResolver $resolver, Traverser $traverser, Ex
$this->stubs = $stubs;
}

public function make(\ReflectionClass $reflection, DeclarationInterface $class, DeclarationInterface $parent): string
/**
* @param \ReflectionClass $reflection
* @param Declaration\DeclarationInterface $class
* @param Declaration\DeclarationInterface $parent
*
* @return string
* @throws \Cycle\ORM\Promise\ProxyFactoryException
*/
public function make(\ReflectionClass $reflection, Declaration\DeclarationInterface $class, Declaration\DeclarationInterface $parent): string
{
$structure = $this->extractor->extract($reflection);
foreach ($structure->methodNames() as $name) {
if (array_key_exists($name, self::PROMISE_METHODS)) {
throw new ProxyFactoryException("Promise method `$name` already defined.");
}
}

$property = $this->resolverPropertyName($structure);
$unsetPropertiesConst = $this->unsetPropertiesConstName($structure);

$visitors = [
new Visitor\AddUseStmts($this->useStmts($class, $parent)),
new Visitor\UpdateNamespace($class->getNamespaceName()),
new Visitor\DeclareClass($class->getShortName(), $parent->getShortName()),
new Visitor\DeclareClass($class->getShortName(), $parent->getShortName(), Utils::shortName(PromiseInterface::class)),
new Visitor\AddUnsetPropertiesConst($unsetPropertiesConst, $structure->properties),
new Visitor\AddResolverProperty($property, $this->propertyType(), $parent->getShortName()),
new Visitor\AddInit(
new Visitor\AddInitMethod(
$property,
$this->propertyType(),
self::DEPENDENCIES,
$this->unsetPropertiesConstName($structure),
$this->initMethodName($structure)
),
new Visitor\AddMagicClone($property, $structure->hasClone),
new Visitor\AddMagicGet($property, self::RESOLVE_METHOD),
new Visitor\AddMagicSet($property, self::RESOLVE_METHOD),
new Visitor\AddMagicIsset($property, self::RESOLVE_METHOD, $unsetPropertiesConst),
new Visitor\AddMagicCloneMethod($property, $structure->hasClone),
new Visitor\AddMagicGetMethod($property, self::RESOLVE_METHOD),
new Visitor\AddMagicSetMethod($property, self::RESOLVE_METHOD),
new Visitor\AddMagicIssetMethod($property, self::RESOLVE_METHOD, $unsetPropertiesConst),
new Visitor\AddMagicUnset($property, self::RESOLVE_METHOD, $unsetPropertiesConst),
new Visitor\AddMagicDebugInfo($property, self::RESOLVE_METHOD, $structure->properties),
new Visitor\AddMagicDebugInfoMethod($property, self::RESOLVE_METHOD, $structure->properties),
new Visitor\UpdatePromiseMethods($property),
new Visitor\AddProxiedMethods($property, $structure->methods, self::RESOLVE_METHOD),
];

foreach (self::PROMISE_METHODS as $method => $returnType) {
$visitors[] = new Visitor\AddPromiseMethod($property, $method, $returnType);
}

$nodes = $this->getNodesFromStub();
$output = $this->traverser->traverseClonedNodes($nodes, ...$visitors);

Expand All @@ -116,22 +139,22 @@ public function make(\ReflectionClass $reflection, DeclarationInterface $class,
);
}

public function initMethodName(Structure $structure): string
public function initMethodName(Declaration\Structure $structure): string
{
return $this->resolver->resolve($structure->methodNames(), self::INIT_METHOD)->fullName();
}

private function resolverPropertyName(Structure $structure): string
private function resolverPropertyName(Declaration\Structure $structure): string
{
return $this->resolver->resolve($structure->properties, self::RESOLVER_PROPERTY)->fullName();
}

private function unsetPropertiesConstName(Structure $structure): string
private function unsetPropertiesConstName(Declaration\Structure $structure): string
{
return $this->resolver->resolve($structure->constants, self::UNSET_PROPERTIES)->fullName('_');
return $this->resolver->resolve($structure->constants, self::UNSET_PROPERTIES_CONST)->fullName('_');
}

private function useStmts(DeclarationInterface $class, DeclarationInterface $parent): array
private function useStmts(Declaration\DeclarationInterface $class, Declaration\DeclarationInterface $parent): array
{
$useStmts = self::USE_STMTS;
if ($class->getNamespaceName() !== $parent->getNamespaceName()) {
Expand Down
14 changes: 7 additions & 7 deletions src/Stubs.php
Expand Up @@ -5,15 +5,15 @@

class Stubs
{
private const FILENAME = 'stubs' . DIRECTORY_SEPARATOR . 'ProxyStub.php';

public function getContent(): string
{
return file_get_contents($this->getStubFilename());
}
$lines = [
'<?php',
'declare(strict_types=1);',
'namespace StubNamespace;',
'class ProxyStub {}'
];

private function getStubFilename(): string
{
return dirname(__DIR__) . DIRECTORY_SEPARATOR . self::FILENAME;
return join("\n", $lines);
}
}
2 changes: 1 addition & 1 deletion src/Traverser.php
Expand Up @@ -10,7 +10,7 @@
final class Traverser
{
/**
* @param Node\Stmt[] $nodes
* @param Node[] $nodes
* @param NodeVisitor ...$visitors
*
* @return Node[]
Expand Down
14 changes: 7 additions & 7 deletions src/Visitor/AddInit.php → src/Visitor/AddInitMethod.php
Expand Up @@ -4,14 +4,12 @@
namespace Cycle\ORM\Promise\Visitor;

use Cycle\ORM\Promise\Expressions;
use Cycle\ORM\Promise\Utils;
use PhpParser\Builder;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

/**
* Add constructor
*/
class AddInit extends NodeVisitorAbstract
class AddInitMethod extends NodeVisitorAbstract
{
/** @var string */
private $property;
Expand Down Expand Up @@ -50,9 +48,11 @@ public function leaveNode(Node $node)
if ($node instanceof Node\Stmt\Class_) {
$method = new Builder\Method($this->initMethod);
$method->makePublic();
$method->addParam((new Builder\Param('orm'))->setType('ORMInterface'));
$method->addParam((new Builder\Param('role'))->setType('string'));
$method->addParam((new Builder\Param('scope'))->setType('array'));
foreach ($this->dependencies as $name => $type) {
if ($type !== null) {
$method->addParam((new Builder\Param($name))->setType(Utils::shortName($type)));
}
}
$method->addStmt($this->unsetProperties());
$method->addStmt($this->assignResolverProperty());

Expand Down
Expand Up @@ -8,7 +8,7 @@
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class AddMagicClone extends NodeVisitorAbstract
class AddMagicCloneMethod extends NodeVisitorAbstract
{
/** @var string */
private $resolverProperty;
Expand Down
Expand Up @@ -8,7 +8,7 @@
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class AddMagicDebugInfo extends NodeVisitorAbstract
class AddMagicDebugInfoMethod extends NodeVisitorAbstract
{
/** @var string */
private $resolverProperty;
Expand Down
Expand Up @@ -8,7 +8,7 @@
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class AddMagicGet extends NodeVisitorAbstract
class AddMagicGetMethod extends NodeVisitorAbstract
{
/** @var string */
private $resolverProperty;
Expand Down
Expand Up @@ -8,7 +8,7 @@
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class AddMagicIsset extends NodeVisitorAbstract
class AddMagicIssetMethod extends NodeVisitorAbstract
{
/** @var string */
private $resolverProperty;
Expand Down
Expand Up @@ -8,7 +8,7 @@
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class AddMagicSet extends NodeVisitorAbstract
class AddMagicSetMethod extends NodeVisitorAbstract
{
/** @var string */
private $resolverProperty;
Expand Down
46 changes: 46 additions & 0 deletions src/Visitor/AddPromiseMethod.php
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);

namespace Cycle\ORM\Promise\Visitor;

use Cycle\ORM\Promise\Expressions;
use Cycle\ORM\Promise\PHPDoc;
use PhpParser\Builder;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class AddPromiseMethod extends NodeVisitorAbstract
{
/** @var string */
private $resolverProperty;

/** @var string */
private $name;

/** @var string|null */
private $returnType;

public function __construct(string $resolverProperty, string $name, string $returnType = null)
{
$this->resolverProperty = $resolverProperty;
$this->name = $name;
$this->returnType = $returnType;
}

public function leaveNode(Node $node)
{
if ($node instanceof Node\Stmt\Class_) {
$method = new Builder\Method($this->name);
$method->makePublic();
$method->addStmt(new Node\Stmt\Return_(Expressions::resolveMethodCall('this', $this->resolverProperty, $this->name)));
if ($this->returnType !== null) {
$method->setReturnType($this->returnType);
}
$method->setDocComment(PHPDoc::writeInheritdoc());

$node->stmts[] = $method->getNode();
}

return null;
}
}
21 changes: 20 additions & 1 deletion src/Visitor/DeclareClass.php
Expand Up @@ -17,10 +17,14 @@ class DeclareClass extends NodeVisitorAbstract
/** @var string */
private $extends;

public function __construct(string $name, string $extends)
/** @var string */
private $implements;

public function __construct(string $name, string $extends, string $implements)
{
$this->name = $name;
$this->extends = $extends;
$this->implements = $implements;
}

/**
Expand All @@ -31,8 +35,23 @@ public function leaveNode(Node $node)
if ($node instanceof Node\Stmt\Class_) {
$node->extends = new Node\Name($this->extends);
$node->name->name = $this->name;
if ($this->canBeImplemented($node)) {
$node->implements[] = new Node\Name($this->implements);
}
}

return null;
}

private function canBeImplemented(Node\Stmt\Class_ $node): bool
{
foreach ($node->implements as $implement) {
$name = join('\\', $implement->parts);
if ($name === $this->implements) {
return false;
}
}

return true;
}
}

0 comments on commit 57dad75

Please sign in to comment.