Skip to content

Commit

Permalink
Factory::fromClassReflection() added option to materialize traits or …
Browse files Browse the repository at this point in the history
…not [Closes #89]
  • Loading branch information
dg committed Sep 18, 2021
1 parent 479c2a7 commit 372fdcb
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 14 deletions.
4 changes: 2 additions & 2 deletions src/PhpGenerator/ClassType.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ public static function enum(string $name = null, PhpNamespace $namespace = null)
/**
* @param string|object $class
*/
public static function from($class): self
public static function from($class, bool $withBodies = false, bool $materializeTraits = true): self
{
return (new Factory)->fromClassReflection(new \ReflectionClass($class));
return (new Factory)->fromClassReflection(new \ReflectionClass($class), $withBodies, $materializeTraits);
}


Expand Down
36 changes: 26 additions & 10 deletions src/PhpGenerator/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Nette\PhpGenerator;

use Nette;
use Nette\Utils\Reflection;


/**
Expand All @@ -23,8 +24,11 @@ final class Factory
private $extractorCache = [];


public function fromClassReflection(\ReflectionClass $from, bool $withBodies = false): ClassType
{
public function fromClassReflection(
\ReflectionClass $from,
bool $withBodies = false,
bool $materializeTraits = true
): ClassType {
if ($withBodies && $from->isAnonymous()) {
throw new Nette\NotSupportedException('The $withBodies parameter cannot be used for anonymous functions.');
}
Expand Down Expand Up @@ -66,8 +70,12 @@ public function fromClassReflection(\ReflectionClass $from, bool $withBodies = f

$props = [];
foreach ($from->getProperties() as $prop) {
$declaringClass = $materializeTraits
? $prop->getDeclaringClass()
: Reflection::getPropertyDeclaringClass($prop);

if ($prop->isDefault()
&& $prop->getDeclaringClass()->name === $from->name
&& $declaringClass->name === $from->name
&& (PHP_VERSION_ID < 80000 || !$prop->isPromoted())
&& !$class->isEnum()
) {
Expand All @@ -78,24 +86,32 @@ public function fromClassReflection(\ReflectionClass $from, bool $withBodies = f

$methods = [];
foreach ($from->getMethods() as $method) {
$realMethod = Reflection::getMethodDeclaringMethod($method);
$declaringClass = ($materializeTraits ? $method : $realMethod)->getDeclaringClass();

if (
$method->getDeclaringClass()->name === $from->name
$declaringClass->name === $from->name
&& (!$enumIface || !method_exists($enumIface, $method->name))
) {
$methods[] = $m = $this->fromMethodReflection($method);
if ($withBodies) {
$srcMethod = Nette\Utils\Reflection::getMethodDeclaringMethod($method);
$srcClass = $srcMethod->getDeclaringClass();
$bodies = &$this->bodyCache[$srcClass->name];
$bodies = $bodies ?? $this->getExtractor($srcClass)->extractMethodBodies($srcClass->name);
if (isset($bodies[$srcMethod->name])) {
$m->setBody($bodies[$srcMethod->name]);
$realMethodClass = $realMethod->getDeclaringClass();
$bodies = &$this->bodyCache[$realMethodClass->name];
$bodies = $bodies ?? $this->getExtractor($realMethodClass)->extractMethodBodies($realMethodClass->name);
if (isset($bodies[$realMethod->name])) {
$m->setBody($bodies[$realMethod->name]);
}
}
}
}
$class->setMethods($methods);

if (!$materializeTraits) {
foreach ($from->getTraitNames() as $trait) {
$class->addTrait($trait);
}
}

$consts = $cases = [];
foreach ($from->getReflectionConstants() as $const) {
if ($class->isEnum() && $from->hasCase($const->name)) {
Expand Down
18 changes: 16 additions & 2 deletions tests/PhpGenerator/ClassType.from.trait.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,25 @@ $res = array_map(function ($class) {
return ClassType::from($class);
}, $classes);

sameFile(__DIR__ . '/expected/ClassType.from.trait.expect', implode("\n", $res));
sameFile(__DIR__ . '/expected/ClassType.from.trait-materialize.expect', implode("\n", $res));


$res = array_map(function ($class) {
return ClassType::withBodiesFrom($class);
}, $classes);

sameFile(__DIR__ . '/expected/ClassType.from.trait.bodies.expect', implode("\n", $res));
sameFile(__DIR__ . '/expected/ClassType.from.trait-materialize.bodies.expect', implode("\n", $res));


$res = array_map(function ($class) {
return ClassType::from($class, /*withBodies:*/ false, /*materializeTraits:*/ false);
}, $classes);

sameFile(__DIR__ . '/expected/ClassType.from.trait-use.expect', implode("\n", $res));


$res = array_map(function ($class) {
return ClassType::from($class, /*withBodies:*/ true, /*materializeTraits:*/ false);
}, $classes);

sameFile(__DIR__ . '/expected/ClassType.from.trait-use.bodies.expect', implode("\n", $res));
91 changes: 91 additions & 0 deletions tests/PhpGenerator/expected/ClassType.from.trait-use.bodies.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Trait1
*/
trait Trait1
{
public static $s1;
public $x1;


public function f1()
{
echo 'Trait1::f1';
}
}

trait Trait1b
{
public function f1()
{
echo 'Trait1b::f1';
}
}

trait Trait2
{
use Trait1;

protected $x2;


public function f2()
{
echo 'Trait2::f2';
}
}

class ParentClass
{
public $x1;


public function f1()
{
echo 'ParentClass::f1';
}
}

class Class1 extends ParentClass
{
use Trait2;
}

class Class2 extends ParentClass
{
use Trait2;

public function f1()
{
echo 'Class2::f1';
}
}

class Class3 extends ParentClass
{
use Trait2;

/** info */
public $x1;


public function f1()
{
echo 'Class3::f1';
}
}

class Class4 extends ParentClass
{
use Trait2;

public function aliased()
{
echo 'Class4::aliased';
}
}

class Class5
{
use Trait1;
use Trait1b;
}
84 changes: 84 additions & 0 deletions tests/PhpGenerator/expected/ClassType.from.trait-use.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* Trait1
*/
trait Trait1
{
public static $s1;
public $x1;


public function f1()
{
}
}

trait Trait1b
{
public function f1()
{
}
}

trait Trait2
{
use Trait1;

protected $x2;


public function f2()
{
}
}

class ParentClass
{
public $x1;


public function f1()
{
}
}

class Class1 extends ParentClass
{
use Trait2;
}

class Class2 extends ParentClass
{
use Trait2;

public function f1()
{
}
}

class Class3 extends ParentClass
{
use Trait2;

/** info */
public $x1;


public function f1()
{
}
}

class Class4 extends ParentClass
{
use Trait2;

public function aliased()
{
}
}

class Class5
{
use Trait1;
use Trait1b;
}

0 comments on commit 372fdcb

Please sign in to comment.