diff --git a/src/Factory/Reflection/ReflectionCompositeFactory.php b/src/Factory/Reflection/ReflectionCompositeFactory.php index 60f30a2..28d7e29 100644 --- a/src/Factory/Reflection/ReflectionCompositeFactory.php +++ b/src/Factory/Reflection/ReflectionCompositeFactory.php @@ -16,6 +16,11 @@ use Spaark\CompositeUtils\Factory\BaseFactory; use Spaark\CompositeUtils\Model\Reflection\ReflectionComposite; +use Spaark\CompositeUtils\Model\Reflection\ReflectionProperty; +use Spaark\CompositeUtils\Model\Reflection\ReflectionMethod; +use Spaark\CompositeUtils\Model\Collection\FixedList; +use Spaark\CompositeUtils\Service\ReflectionCompositeProviderInterface; +use Spaark\CompositeUtils\Service\ReflectionCompositeProvider; use \ReflectionClass as PHPNativeReflectionClass; use \ReflectionProperty as PHPNativeReflectionProperty; use \ReflectionMethod as PHPNativeReflectionMethod; @@ -28,6 +33,13 @@ class ReflectionCompositeFactory extends ReflectorFactory { const REFLECTION_OBJECT = ReflectionComposite::class; + const PROPERTIES = [ + 'traits' => [''], + 'interfaces' => [''], + 'Methods' => ['local'], + 'Properties' => ['local', 'required', 'optional', 'built'] + ]; + /** * @var PHPNativeReflector */ @@ -38,6 +50,11 @@ class ReflectionCompositeFactory extends ReflectorFactory */ protected $object; + /** + * @var ReflectionCompositeProviderInterface + */ + protected $provider; + /** * Creates a new ReflectionCompositeFactory from the given * classname @@ -47,7 +64,28 @@ class ReflectionCompositeFactory extends ReflectorFactory */ public static function fromClassName(string $classname) { - return new static(new PHPNativeReflectionClass($classname)); + return new static + ( + new PHPNativeReflectionClass($classname), + ReflectionCompositeProvider::getDefault() + ); + } + + /** + * Constructs the Factory with the given reflector and Composite + * provider + * + * @param PHPNativeReflectionClass $reflect + * @param ReflectionCompositeProviderInterface $provider + */ + public function __construct + ( + PHPNativeReflectionClass $reflect, + ReflectionCompositeProviderInterface $provider + ) + { + parent::__construct($reflect); + $this->provider = $provider; } /** @@ -57,10 +95,28 @@ public static function fromClassName(string $classname) */ public function build() { - $file = - (new ReflectionFileFactory($this->reflector->getFileName())) - ->build(); + $this->initObject(); + + foreach ($this->reflector->getTraits() as $trait) + { + $this->addInheritance('traits', $trait); + } + + if ($parent = $this->reflector->getParentClass()) + { + $this->addInheritance('parent', $parent, 'setRawValue'); + } + + foreach ($this->reflector->getInterfaces() as $interface) + { + $this->addInheritance('interfaces', $interface); + } + + $fileName = $this->reflector->getFileName(); + + $file = (new ReflectionFileFactory($fileName))->build(); $this->accessor->setRawValue('file', $file); + $this->accessor->setRawValue ( 'classname', @@ -72,71 +128,151 @@ public function build() $file->namespaces[$this->reflector->getNamespaceName()] ); - foreach ($this->reflector->getProperties() as $property) + $this->addItems('properties', false, 'Property'); + $this->addItems('methods', true, 'Method'); + + $this->resizeProperties(); + + return $this->object; + } + + /** + * Initialise the object with fixed lists + */ + protected function initObject() + { + foreach (static::PROPERTIES as $name => $prefixes) { - if ($this->checkIfLocal($property)) + $size = count($this->reflector->{'get' . $name}()); + foreach ($prefixes as $prefix) { - $this->buildProperty($property); + $this->accessor->setRawValue + ( + $prefix . $name, + new FixedList($size) + ); } } + } - foreach ($this->reflector->getMethods() as $method) + /** + * Resize the FixedList properties down to their size + */ + protected function resizeProperties() + { + foreach (static::PROPERTIES as $name => $prefixes) { - if ($this->checkIfLocal($method)) + foreach ($prefixes as $prefix) { - $this->buildMethod($method); + $this->object->{$prefix . $name}->resizeToFull(); } } - - return $this->object; } /** - * Uses a ReflectionPropertyFactory to build a ReflectionProperty, - * and adds that to this ReflectionComposite + * Loops through the list methods or properties adding them to the + * Composite * - * @param PHPNativeReflectionProperty + * @param string $name + * @param bool $checkFile + * @param string $singular */ - protected function buildProperty + protected function addItems ( - PHPNativeReflectionProperty $reflect + string $name, + bool $checkFile, + string $signular ) { - $properties = $this->accessor->getRawValue('properties'); - - $properties[$reflect->getName()] = - (new ReflectionPropertyFactory($reflect)) - ->build + foreach ($this->reflector->{'get' . $name}() as $item) + { + // We only reflect on methods in userspace + if ($checkFile && !$item->getFileName()) + { + continue; + } + // This belongs to a super class, use that definition + // instead + elseif ($item->class !== $this->reflector->getName()) + { + $item = $this->provider->get($item->class) + ->$name[$item->getName()]; + } + // Parse this method + else + { + $factory = + '\Spaark\CompositeUtils\Factory\Reflection' + . '\Reflection' . $signular . 'Factory'; + $item = $this->{'build' . $signular} + ( + new $factory($item), + $item + ); + $this->accessor->rawAddToValue ( - $this->object, - $this->reflector - ->getDefaultProperties()[$reflect->getName()] + 'local' . ucfirst($name), + $item ); + } + + $this->accessor->getRawValue($name)[$item->name] = $item; + } + } + + /** + * Adds a super class / interface / trait to this Composite + * + * @param string $group The type of superclass (parent, etc...) + * @param PHPNativeReflectionClass $reflect + * @param string $method + */ + protected function addInheritance + ( + string $group, + PHPNativeReflectionClass $reflect, + string $method = 'rawAddToValue' + ) + { + // We only reflect on classes within userspace + if ($reflect->getFileName()) + { + $item = $this->provider->get($reflect->getName()); + $this->accessor->$method($group, $item); + } } /** - * Uses a ReflectionMethodFactory to build a ReflectionMethod, and - * adds that to this ReflectionComposite + * Uses a ReflectionPropertyFactory to build a ReflectionProperty * - * @param PHPNativeReflectionMethod + * @param ReflectionPropertyFactory $factory + * @return ReflectionProperty */ - protected function buildMethod(PHPNativeReflectionMethod $reflect) + protected function buildProperty + ( + ReflectionPropertyFactory $factory, + PHPNativeReflectionProperty $reflect + ) + : ReflectionProperty { - $methods = $this->accessor->getRawValue('methods'); - $methods[$reflect->getName()] = - (new ReflectionMethodFactory($reflect)) - ->build($this->object); + return $factory->build + ( + $this->object, + $this->reflector + ->getDefaultProperties()[$reflect->getName()] + ); } /** - * Checks if a property is defined in the class + * Uses a ReflectionMethodFactory to build a ReflectionMethod * - * @param Reflector $reflector - * @return boolean + * @param ReflectionMethodFactory $factory + * @return ReflectionMethod */ - protected function checkIfLocal(Reflector $reflector) + protected function buildMethod(ReflectionMethodFactory $factory) + : ReflectionMethod { - return $reflector->class === $this->reflector->getName(); + return $factory->build($this->object); } } diff --git a/src/Model/Collection/AbstractList.php b/src/Model/Collection/AbstractList.php index 42f66a6..d116f77 100644 --- a/src/Model/Collection/AbstractList.php +++ b/src/Model/Collection/AbstractList.php @@ -28,7 +28,7 @@ public function offsetSet($offset, $value) { if ($offset === null) { - $this->push($value); + $this->add($value); } else { diff --git a/src/Model/Collection/ArrayList.php b/src/Model/Collection/ArrayList.php index d88e3d8..9b977ae 100644 --- a/src/Model/Collection/ArrayList.php +++ b/src/Model/Collection/ArrayList.php @@ -29,7 +29,7 @@ class ArrayList extends AbstractList /** * {@inheritDoc} */ - public function push($item) + public function add($item) { $this->data[] = $item; } diff --git a/src/Model/Collection/FixedList.php b/src/Model/Collection/FixedList.php index 1f13283..98354dc 100644 --- a/src/Model/Collection/FixedList.php +++ b/src/Model/Collection/FixedList.php @@ -40,7 +40,7 @@ public function __construct(int $size = 0) /** * {@inheritDoc} */ - public function push($item) + public function add($item) { $this->data[$this->pointer++] = $item; } @@ -102,5 +102,33 @@ public function size() : int { return count($this->data); } + + /** + * Resizes the FixedList, throwing away any unused elements + * + * @param int $size The new size + */ + public function resize(int $size) + { + $this->data->setSize($size); + } + + /** + * Returns the current pointer position + * + * @return int + */ + public function getCurrentPosition() : int + { + return $this->pointer; + } + + /** + * Resizes to the current pointer + */ + public function resizeToFull() + { + $this->resize($this->getCurrentPosition()); + } } diff --git a/src/Model/Collection/ListInterface.php b/src/Model/Collection/ListInterface.php index a681068..c0abea1 100644 --- a/src/Model/Collection/ListInterface.php +++ b/src/Model/Collection/ListInterface.php @@ -24,7 +24,7 @@ interface ListInterface extends CollectionInterface * * @param ValueType $item The item to add */ - public function push($item); + public function add($item); /** * Adds a new item at the specified position diff --git a/src/Model/Reflection/ReflectionComposite.php b/src/Model/Reflection/ReflectionComposite.php index 254c9c2..92e94ff 100644 --- a/src/Model/Reflection/ReflectionComposite.php +++ b/src/Model/Reflection/ReflectionComposite.php @@ -15,16 +15,16 @@ namespace Spaark\CompositeUtils\Model\Reflection; use Spaark\CompositeUtils\Model\Collection\HashMap; -use Spaark\CompositeUtils\Model\Collection\ArrayList; +use Spaark\CompositeUtils\Model\Collection\FixedList; /** * Represents a composite class * * @property-read HashMap $properties - * @property-read ArrayList $requiredProperties - * @property-read ArrayList $optionalProperties - * @property-read ArrayList $builtProperties + * @property-read FixedList $requiredProperties + * @property-read FixedList $optionalProperties + * @property-read FixedList $builtProperties * @property-read HashMap $methods * @property-read ReflectionFile $file * @property-read NamespaceBlock $namespace @@ -32,6 +32,21 @@ */ class ReflectionComposite extends Reflector { + /** + * @var ?ReflectionComposite + */ + protected $parent; + + /** + * @var FixedList + */ + protected $traits; + + /** + * @var FixedList + */ + protected $interfaces; + /** * The properties within the composite * @@ -39,10 +54,17 @@ class ReflectionComposite extends Reflector */ protected $properties; + /** + * Properties local to this composite + * + * @var FixedList + */ + protected $localProperties; + /** * The properties which are required in the composite's constructor * - * @var ArrayList + * @var FixedList */ protected $requiredProperties; @@ -50,7 +72,7 @@ class ReflectionComposite extends Reflector * The properties which can be optionally passed to the composite's * constructor * - * @var ArrayList + * @var FixedList */ protected $optionalProperties; @@ -58,7 +80,7 @@ class ReflectionComposite extends Reflector * The properties which will be built without input in the * composite's constructor * - * @var ArrayList + * @var FixedList */ protected $builtProperties; @@ -69,6 +91,13 @@ class ReflectionComposite extends Reflector */ protected $methods; + /** + * Method local to this composite + * + * @var FixedList + */ + protected $localMethods; + /** * The file in which this composite was declared * @@ -91,15 +120,16 @@ class ReflectionComposite extends Reflector protected $classname; /** - * Creates the ReflectionComposite by initializing its ArrayList + * Creates the ReflectionComposite by initializing its FixedList * properties + * + * As the ReflectionComposite use a requirement of the AutoConstruct + * feature, this class is not able to make use of it (as it would + * create an unresolvable circular dependancy) */ public function __construct() { $this->properties = new HashMap(); $this->methods = new HashMap(); - $this->requiredProperties = new ArrayList(); - $this->optionalProperties = new ArrayList(); - $this->builtProperties = new ArrayList(); } } diff --git a/src/Service/RawPropertyAccessor.php b/src/Service/RawPropertyAccessor.php index 575e8f1..39d803c 100644 --- a/src/Service/RawPropertyAccessor.php +++ b/src/Service/RawPropertyAccessor.php @@ -105,7 +105,7 @@ public function rawAddToValue($key, $value) $key, CannotWritePropertyException::class ); - $property->getValue($this->object)->push($value); + $property->getValue($this->object)->add($value); } /** diff --git a/src/Service/ReflectionCompositeProvider.php b/src/Service/ReflectionCompositeProvider.php new file mode 100644 index 0000000..b44dc09 --- /dev/null +++ b/src/Service/ReflectionCompositeProvider.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the + * LICENSE.md file that was distributed with this source code. + * + * @package spaark/composite-utils + * @author Emily Shepherd + * @license MIT + */ + +namespace Spaark\CompositeUtils\Service; + +use Spaark\CompositeUtils\Model\Reflection\ReflectionComposite; +use Spaark\CompositeUtils\Factory\Reflection\ReflectionCompositeFactory; +use Spaark\CompositeUtils\Model\Collection\HashMap; + +/** + */ +class ReflectionCompositeProvider + implements ReflectionCompositeProviderInterface +{ + /** + * @var ReflectionCompositeProviderInterface + */ + protected static $default; + + public static function getDefault() + : ReflectionCompositeProviderInterface + { + if (!static::$default) + { + static::$default = new static(); + } + + return static::$default; + } + + public static function setDefault + ( + ReflectionCompositeProviderInterface $default + ) + { + static::$default = $default; + } + + private $cache; + + public function __construct() + { + $this->cache = new HashMap(); + } + + public function get(string $classname) : ReflectionComposite + { + if (!$this->cache->containsKey($classname)) + { + $this->cache[$classname] = + ( + ReflectionCompositeFactory::fromClassName + ( + $classname + ) + ) + ->build(); + } + + return $this->cache[$classname]; + } +} diff --git a/src/Service/ReflectionCompositeProviderInterface.php b/src/Service/ReflectionCompositeProviderInterface.php new file mode 100644 index 0000000..d17b8b8 --- /dev/null +++ b/src/Service/ReflectionCompositeProviderInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the + * LICENSE.md file that was distributed with this source code. + * + * @package spaark/composite-utils + * @author Emily Shepherd + * @license MIT + */ + +namespace Spaark\CompositeUtils\Service; + +use Spaark\CompositeUtils\Model\Reflection\ReflectionComposite; + +/** + */ +interface ReflectionCompositeProviderInterface +{ + /** + * Returns a ReflectionComposite + * + * @param string $classname + * @return ReflectionComposite + */ + public function get(string $classname) : ReflectionComposite; +} diff --git a/test/Factory/ReflectionFactoryTest.php b/test/Factory/ReflectionFactoryTest.php index e1babc7..a2583e0 100644 --- a/test/Factory/ReflectionFactoryTest.php +++ b/test/Factory/ReflectionFactoryTest.php @@ -16,6 +16,7 @@ use Spaark\CompositeUtils\Factory\Reflection\ReflectionCompositeFactory; use Spaark\CompositeUtils\Test\Model\TestEntity; +use Spaark\CompositeUtils\Test\Model\InheritedEntity; use PHPUnit\Framework\TestCase; use Spaark\CompositeUtils\Model\Reflection\ReflectionComposite; use Spaark\CompositeUtils\Model\Reflection\ReflectionProperty; @@ -44,11 +45,11 @@ class ReflectionFactoryTest extends TestCase ['prop5', ObjectType::class, false, false, false, false, false, false] ]; - public function testComposite() + public function testComposite(string $classname = TestEntity::class) { $reflect = ReflectionCompositeFactory::fromClassName ( - TestEntity::class + $classname ) ->build(); @@ -59,7 +60,7 @@ public function testComposite() $this->assertAttributeCount(1, 'methods', $reflect); $this->assertAttributeEquals ( - TestEntity::class, 'classname', $reflect + $classname, 'classname', $reflect ); return $reflect; @@ -158,6 +159,11 @@ public function testConstructorItems(ReflectionComposite $reflect) )); } + public function testInheritance() + { + $this->testComposite(InheritedEntity::class); + } + public function propertiesProvider() { return $this->properties; diff --git a/test/Model/Collection/ArrayListTest.php b/test/Model/Collection/ArrayListTest.php index 5fe8a9a..ba11485 100644 --- a/test/Model/Collection/ArrayListTest.php +++ b/test/Model/Collection/ArrayListTest.php @@ -32,10 +32,10 @@ public function testEmpty() } } - public function testPush() + public function testAdd() { $collection = new ArrayList(); - $collection->push('Value'); + $collection->add('Value'); $this->assertFalse($collection->empty()); $this->assertEquals(1, $collection->size()); } @@ -43,7 +43,7 @@ public function testPush() public function testOffsetGet() { $collection = new ArrayList(); - $collection->push('123'); + $collection->add('123'); $this->assertEquals('123', $collection->get(0)); $collection[] = '456'; diff --git a/test/Model/Collection/FixedListTest.php b/test/Model/Collection/FixedListTest.php index dbeed6f..f36f178 100644 --- a/test/Model/Collection/FixedListTest.php +++ b/test/Model/Collection/FixedListTest.php @@ -39,7 +39,7 @@ public function testFixedSize() for ($i = 0; $i < 10; $i++) { - $collection->push($i); + $collection->add($i); } $this->assertEquals(10, $collection->size()); @@ -47,6 +47,24 @@ public function testFixedSize() $this->assertEquals(10, $collection->size()); } + public function testResize() + { + $collection = new FixedList(10); + $collection->resize(20); + + $this->assertEquals(20, $collection->size()); + } + + public function testResizeToFull() + { + $collection = new FixedList(10); + $collection[] = 1; + $collection[] = 2; + $collection[] = 3; + $collection->resizeToFull(); + $this->assertEquals(3, $collection->size()); + } + public function testSet() { $collection = new FixedList(5); @@ -61,7 +79,7 @@ public function testSet() public function testOffsetGet() { $collection = new FixedList(2); - $collection->push('123'); + $collection->add('123'); $this->assertEquals('123', $collection->get(0)); $collection[] = '456'; diff --git a/test/Model/InheritedEntity.php b/test/Model/InheritedEntity.php new file mode 100644 index 0000000..f476bea --- /dev/null +++ b/test/Model/InheritedEntity.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the + * LICENSE.md file that was distributed with this source code. + * + * @package spaark/composite-utils + * @author Emily Shepherd + * @license MIT + */ + +namespace Spaark\CompositeUtils\Test\Model; + +use Spaark\CompositeUtils\Traits\AllReadableTrait; +use Spaark\CompositeUtils\Traits\AutoConstructTrait; + +class InheritedEntity extends TestEntity +{ +}