Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions src/LeanMapper/DefaultEntityReflectionProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

/**
* This file is part of the Lean Mapper library (http://www.leanmapper.com)
*
* Copyright (c) 2013 Vojtěch Kohout (aka Tharos)
*
* For the full copyright and license information, please view the file
* license.md that was distributed with this source code.
*/

namespace LeanMapper;

use LeanMapper\Reflection\AnnotationsParser;
use LeanMapper\Reflection\Property;
use LeanMapper\Reflection\PropertyFactory;
use ReflectionClass;
use ReflectionMethod;

class DefaultEntityReflectionProvider implements IEntityReflectionProvider
{

/** @var array */
protected $internalGetters = ['getData', 'getRowData', 'getModifiedRowData', 'getCurrentReflection', 'getReflection', 'getHasManyRowDifferences', 'getEntityClass'];



/**
* @return Property[]
*/
public function getProperties(ReflectionClass $entityClass, IMapper $mapper = null)
{
$properties = [];
$annotationTypes = ['property', 'property-read'];
foreach ($this->getFamilyLine($entityClass) as $member) {
foreach ($annotationTypes as $annotationType) {
foreach (AnnotationsParser::parseMultiLineAnnotationValues($annotationType, $member->getDocComment()) as $definition) {
$properties[] = PropertyFactory::createFromAnnotation($annotationType, $definition, $member, $mapper);
}
}
}
return $properties;
}



/**
* @return ReflectionMethod[]
*/
public function getGetters(ReflectionClass $entityClass)
{
$getters = [];
foreach ($entityClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
$name = $method->getName();
if (strlen($name) > 3 && substr($name, 0, 3) === 'get') {
$getters[$name] = $method;
}
}
return array_diff_key($getters, array_flip($this->internalGetters));
}



/**
* @return ReflectionMethod[]
*/
public function getSetters(ReflectionClass $entityClass)
{
$setters = [];
foreach ($entityClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
$name = $method->getName();
if (strlen($name) > 3 && substr($name, 0, 3) === 'set') {
$setters[$name] = $method;
}
}
return $setters;
}



/**
* @return ReflectionClass[]
*/
protected function getFamilyLine(ReflectionClass $member)
{
$line = [$member];
while ($member = $member->getParentClass()) {
if ($member->name === 'LeanMapper\Entity') {
break;
}
$line[] = $member;
}
return array_reverse($line);
}

}
18 changes: 17 additions & 1 deletion src/LeanMapper/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,29 @@ public static function getReflection(IMapper $mapper = null)
$class = get_called_class();
$mapperClass = $mapper !== null ? get_class($mapper) : '';
if (!isset(static::$reflections[$class][$mapperClass])) {
static::$reflections[$class][$mapperClass] = new EntityReflection($class, $mapper);
static::$reflections[$class][$mapperClass] = new EntityReflection($class, $mapper, static::getReflectionProvider());
}
return static::$reflections[$class][$mapperClass];
}



/**
* @return IEntityReflectionProvider
*/
protected static function getReflectionProvider()
{
static $reflectionProvider = null;

if ($reflectionProvider === null) {
$reflectionProvider = new DefaultEntityReflectionProvider;
}

return $reflectionProvider;
}



/**
* @param Row|Traversable|array|null $arg
* @throws InvalidArgumentException
Expand Down
40 changes: 40 additions & 0 deletions src/LeanMapper/IEntityReflectionProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

/**
* This file is part of the Lean Mapper library (http://www.leanmapper.com)
*
* Copyright (c) 2013 Vojtěch Kohout (aka Tharos)
*
* For the full copyright and license information, please view the file
* license.md that was distributed with this source code.
*/

namespace LeanMapper;

use LeanMapper\Reflection\Property;
use ReflectionClass;
use ReflectionMethod;

interface IEntityReflectionProvider
{

/**
* @return Property[]
*/
function getProperties(ReflectionClass $entityClass, IMapper $mapper = null);



/**
* @return ReflectionMethod[]
*/
function getGetters(ReflectionClass $entityClass);



/**
* @return ReflectionMethod[]
*/
function getSetters(ReflectionClass $entityClass);

}
117 changes: 59 additions & 58 deletions src/LeanMapper/Reflection/EntityReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace LeanMapper\Reflection;

use LeanMapper\Exception\InvalidStateException;
use LeanMapper\DefaultEntityReflectionProvider;
use LeanMapper\IEntityReflectionProvider;
use LeanMapper\IMapper;
use ReflectionMethod;

Expand All @@ -26,6 +28,9 @@ class EntityReflection extends \ReflectionClass
/** @var IMapper|null */
private $mapper;

/** @var IEntityReflectionProvider */
private $entityReflectionProvider;

/** @var Property[] */
private $properties = null;

Expand All @@ -41,19 +46,18 @@ class EntityReflection extends \ReflectionClass
/** @var string */
private $docComment;

/** @var array */
private $internalGetters = ['getData', 'getRowData', 'getModifiedRowData', 'getCurrentReflection', 'getReflection', 'getHasManyRowDifferences', 'getEntityClass'];



/**
* @param mixed $argument
* @param IMapper|null $mapper
* @param IEntityReflectionProvider|null $entityReflectionProvider
*/
public function __construct($argument, IMapper $mapper = null)
public function __construct($argument, IMapper $mapper = null, IEntityReflectionProvider $entityReflectionProvider = null)
{
parent::__construct($argument);
$this->mapper = $mapper;
$this->entityReflectionProvider = $entityReflectionProvider !== null ? $entityReflectionProvider : new DefaultEntityReflectionProvider;
}


Expand All @@ -67,7 +71,7 @@ public function __construct($argument, IMapper $mapper = null)
public function getEntityProperty($name)
{
if ($this->properties === null) {
$this->parseProperties();
$this->createProperties();
}
return isset($this->properties[$name]) ? $this->properties[$name] : null;
}
Expand All @@ -82,7 +86,7 @@ public function getEntityProperty($name)
public function getEntityProperties()
{
if ($this->properties === null) {
$this->parseProperties();
$this->createProperties();
}
return $this->properties;
}
Expand Down Expand Up @@ -111,7 +115,7 @@ public function getAliases()
*/
public function getParentClass()
{
return ($reflection = parent::getParentClass()) ? new self($reflection->getName()) : null;
return ($reflection = parent::getParentClass()) ? new self($reflection->getName(), $this->mapper, $this->entityReflectionProvider) : null;
}


Expand Down Expand Up @@ -140,7 +144,7 @@ public function getDocComment()
public function getGetter($name)
{
if ($this->getters === null) {
$this->initGettersAndSetters();
$this->createGetters();
}
return isset($this->getters[$name]) ? $this->getters[$name] : null;
}
Expand All @@ -155,7 +159,7 @@ public function getGetter($name)
public function getGetters()
{
if ($this->getters === null) {
$this->initGettersAndSetters();
$this->createGetters();
}
return $this->getters;
}
Expand All @@ -171,7 +175,7 @@ public function getGetters()
public function getSetter($name)
{
if ($this->setters === null) {
$this->initGettersAndSetters();
$this->createSetters();
}
return isset($this->setters[$name]) ? $this->setters[$name] : null;
}
Expand All @@ -182,71 +186,68 @@ public function getSetter($name)
/**
* @throws InvalidStateException
*/
private function parseProperties()
private function createGetters()
{
$this->properties = [];
$annotationTypes = ['property', 'property-read'];
$columns = [];
foreach ($this->getFamilyLine() as $member) {
foreach ($annotationTypes as $annotationType) {
foreach (AnnotationsParser::parseMultiLineAnnotationValues($annotationType, $member->getDocComment()) as $definition) {
$property = PropertyFactory::createFromAnnotation($annotationType, $definition, $member, $this->mapper);
// collision check
if (isset($this->properties[$property->getName()])) {
throw new InvalidStateException(
"Duplicated property '{$property->getName()}' in entity {$this->getName()}. Please fix property name."
);
}

$column = $property->getColumn();
if ($column !== null and $property->isWritable()) {
if (isset($columns[$column])) {
throw new InvalidStateException(
"Mapping collision in property '{$property->getName()}' (column '$column') in entity {$this->getName()}. Please fix mapping or make chosen properties read only (using property-read)."
);
}
$columns[$column] = true;
}
$this->properties[$property->getName()] = $property;
}
$this->getters = [];
$getters = $this->entityReflectionProvider->getGetters($this);

foreach ($getters as $getter) {
$name = $getter->getName();
// collision check
if (isset($this->getters[$name])) {
throw new InvalidStateException("Duplicated getter '{$name}' for entity {$this->getName()}.");
}
$this->getters[$name] = $getter;
}
}



private function initGettersAndSetters()
/**
* @throws InvalidStateException
*/
private function createSetters()
{
$this->getters = $this->setters = [];
foreach ($this->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
$name = $method->getName();
if (strlen($name) > 3) {
$prefix = substr($name, 0, 3);
if ($prefix === 'get') {
$this->getters[$name] = $method;
} elseif ($prefix === 'set') {
$this->setters[$name] = $method;
}
$this->setters = [];
$setters = $this->entityReflectionProvider->getSetters($this);

foreach ($setters as $setter) {
$name = $setter->getName();
// collision check
if (isset($this->setters[$name])) {
throw new InvalidStateException("Duplicated setter '{$name}' for entity {$this->getName()}.");
}
$this->setters[$name] = $setter;
}
$this->getters = array_diff_key($this->getters, array_flip($this->internalGetters));
}



/**
* @return self[]
* @throws InvalidStateException
*/
private function getFamilyLine()
private function createProperties()
{
$line = [$member = $this];
while ($member = $member->getParentClass()) {
if ($member->name === 'LeanMapper\Entity') {
break;
$this->properties = [];
$properties = $this->entityReflectionProvider->getProperties($this, $this->mapper);
$columns = [];
foreach ($properties as $property) {
// collision check
if (isset($this->properties[$property->getName()])) {
throw new InvalidStateException(
"Duplicated property '{$property->getName()}' in entity {$this->getName()}. Please fix property name."
);
}

$column = $property->getColumn();
if ($column !== null and $property->isWritable()) {
if (isset($columns[$column])) {
throw new InvalidStateException(
"Mapping collision in property '{$property->getName()}' (column '$column') in entity {$this->getName()}. Please fix mapping or make chosen properties read only (using property-read)."
);
}
$columns[$column] = true;
}
$line[] = $member;
$this->properties[$property->getName()] = $property;
}
return array_reverse($line);
}

}
Loading