diff --git a/.gitignore b/.gitignore index 68f849f..c6576ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.idea +/nbproject /_local /vendor /composer.lock diff --git a/src/LeanMapper/Entity.php b/src/LeanMapper/Entity.php index 8475387..8dc6822 100644 --- a/src/LeanMapper/Entity.php +++ b/src/LeanMapper/Entity.php @@ -49,7 +49,7 @@ abstract class Entity protected static $reflections = []; /** @var EntityReflection|null */ - private $currentReflection; + protected $currentReflection; @@ -860,7 +860,7 @@ protected function initDefaults() * @throws InvalidValueException * @return Entity|null */ - private function getHasOneValue(Property $property, Relationship\HasOne $relationship, Filtering $filtering = null) + protected function getHasOneValue(Property $property, Relationship\HasOne $relationship, Filtering $filtering = null) { $targetTable = $relationship->getTargetTable(); $row = $this->row->referenced($targetTable, $relationship->getColumnReferencingTargetTable(), $filtering); @@ -890,7 +890,7 @@ private function getHasOneValue(Property $property, Relationship\HasOne $relatio * @return Entity[] * @throws InvalidValueException */ - private function getHasManyValue( + protected function getHasManyValue( Property $property, Relationship\HasMany $relationship, Filtering $targetTableFiltering = null, @@ -927,7 +927,7 @@ private function getHasManyValue( * @return Entity|null * @throws InvalidValueException */ - private function getBelongsToOneValue(Property $property, Relationship\BelongsToOne $relationship, Filtering $filtering = null) + protected function getBelongsToOneValue(Property $property, Relationship\BelongsToOne $relationship, Filtering $filtering = null) { $targetTable = $relationship->getTargetTable(); $rows = $this->row->referencing($targetTable, $relationship->getColumnReferencingSourceTable(), $filtering, $relationship->getStrategy()); @@ -961,7 +961,7 @@ private function getBelongsToOneValue(Property $property, Relationship\BelongsTo * @param Filtering|null $filtering * @return Entity[] */ - private function getBelongsToManyValue(Property $property, Relationship\BelongsToMany $relationship, Filtering $filtering = null) + protected function getBelongsToManyValue(Property $property, Relationship\BelongsToMany $relationship, Filtering $filtering = null) { $targetTable = $relationship->getTargetTable(); $rows = $this->row->referencing($targetTable, $relationship->getColumnReferencingSourceTable(), $filtering, $relationship->getStrategy()); @@ -985,7 +985,7 @@ private function getBelongsToManyValue(Property $property, Relationship\BelongsT * @throws InvalidMethodCallException * @throws InvalidStateException */ - private function useMapper(IMapper $mapper) + protected function useMapper(IMapper $mapper) { if ($this->mapper === null) { $newProperties = $this->getReflection($mapper)->getEntityProperties(); @@ -1018,7 +1018,7 @@ private function useMapper(IMapper $mapper) * @param IEntityFactory $entityFactory * @throws InvalidStateException */ - private function setEntityFactory(IEntityFactory $entityFactory) + protected function setEntityFactory(IEntityFactory $entityFactory) { if ($this->entityFactory === null) { $this->entityFactory = $entityFactory; @@ -1039,7 +1039,7 @@ private function setEntityFactory(IEntityFactory $entityFactory) * @throws InvalidArgumentException * @throws InvalidValueException */ - private function addToOrRemoveFrom($action, $name, $arg) + protected function addToOrRemoveFrom($action, $name, $arg) { if ($this->isDetached()) { throw new InvalidMethodCallException('Cannot add or remove related entity to detached entity.'); @@ -1108,7 +1108,7 @@ private function addToOrRemoveFrom($action, $name, $arg) * @param Entity $entity * @throws InvalidValueException */ - private function checkConsistency(Property $property, $mapperClass, Entity $entity) + protected function checkConsistency(Property $property, $mapperClass, Entity $entity) { $type = $property->getType(); if (!($entity instanceof $type)) { @@ -1127,7 +1127,7 @@ private function checkConsistency(Property $property, $mapperClass, Entity $enti * @param string $methodName * @throws InvalidMethodCallException */ - private function checkMethodArgumentsCount($expectedCount, array $arguments, $methodName) + protected function checkMethodArgumentsCount($expectedCount, array $arguments, $methodName) { if (count($arguments) !== $expectedCount) { if ($expectedCount === 0) { diff --git a/src/LeanMapper/Events.php b/src/LeanMapper/Events.php index 6877617..211cee6 100644 --- a/src/LeanMapper/Events.php +++ b/src/LeanMapper/Events.php @@ -36,7 +36,7 @@ class Events const EVENT_AFTER_DELETE = 'afterDelete'; /** @var array */ - private $events = [ + protected $events = [ self::EVENT_BEFORE_PERSIST => [], self::EVENT_BEFORE_CREATE => [], self::EVENT_BEFORE_UPDATE => [], @@ -99,7 +99,7 @@ public function &getCallbacksReference($event) * @param string $event * @throws InvalidArgumentException */ - private function checkEventType($event) + protected function checkEventType($event) { if (!isset($this->events[$event])) { throw new InvalidArgumentException("Unknown event type given: '$event'."); diff --git a/src/LeanMapper/Reflection/EntityReflection.php b/src/LeanMapper/Reflection/EntityReflection.php index 07a9e18..8116c1b 100644 --- a/src/LeanMapper/Reflection/EntityReflection.php +++ b/src/LeanMapper/Reflection/EntityReflection.php @@ -24,25 +24,25 @@ class EntityReflection extends \ReflectionClass { /** @var IMapper|null */ - private $mapper; + protected $mapper; /** @var Property[] */ - private $properties = null; + protected $properties = null; /** @var array */ - private $getters = null; + protected $getters = null; /** @var array */ - private $setters = null; + protected $setters = null; /** @var Aliases|null */ - private $aliases; + protected $aliases; /** @var string */ - private $docComment; + protected $docComment; /** @var array */ - private $internalGetters = ['getData', 'getRowData', 'getModifiedRowData', 'getCurrentReflection', 'getReflection', 'getHasManyRowDifferences', 'getEntityClass']; + protected $internalGetters = ['getData', 'getRowData', 'getModifiedRowData', 'getCurrentReflection', 'getReflection', 'getHasManyRowDifferences', 'getEntityClass']; @@ -182,7 +182,7 @@ public function getSetter($name) /** * @throws InvalidStateException */ - private function parseProperties() + protected function parseProperties() { $this->properties = []; $annotationTypes = ['property', 'property-read']; @@ -215,7 +215,7 @@ private function parseProperties() - private function initGettersAndSetters() + protected function initGettersAndSetters() { $this->getters = $this->setters = []; foreach ($this->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { @@ -237,7 +237,7 @@ private function initGettersAndSetters() /** * @return self[] */ - private function getFamilyLine() + protected function getFamilyLine() { $line = [$member = $this]; while ($member = $member->getParentClass()) { diff --git a/src/LeanMapper/Reflection/Property.php b/src/LeanMapper/Reflection/Property.php index ef6f0e2..9200154 100644 --- a/src/LeanMapper/Reflection/Property.php +++ b/src/LeanMapper/Reflection/Property.php @@ -24,49 +24,49 @@ class Property { /** @var string */ - private $name; + protected $name; /** @var string|null */ - private $column; + protected $column; /** @var EntityReflection */ - private $entityReflection; + protected $entityReflection; /** @var PropertyType */ - private $type; + protected $type; /** @var bool */ - private $isWritable; + protected $isWritable; /** @var bool */ - private $isNullable; + protected $isNullable; /** @var bool */ - private $hasDefaultValue; + protected $hasDefaultValue; /** @var mixed|null */ - private $defaultValue; + protected $defaultValue; /** @var bool */ - private $containsCollection; + protected $containsCollection; /** @var Relationship\HasOne|Relationship\HasMany|Relationship\BelongsToOne|Relationship\BelongsToMany|null */ - private $relationship; + protected $relationship; /** @var PropertyMethods|null */ - private $propertyMethods; + protected $propertyMethods; /** @var PropertyFilters|null */ - private $propertyFilters; + protected $propertyFilters; /** @var PropertyPasses|null */ - private $propertyPasses; + protected $propertyPasses; /** @var PropertyValuesEnum|null */ - private $propertyValuesEnum; + protected $propertyValuesEnum; /** @var array */ - private $customFlags; + protected $customFlags; @@ -438,7 +438,7 @@ public function getCustomFlagValue($name) /** * @throws InvalidMethodCallException */ - private function checkContainsEnumeration() + protected function checkContainsEnumeration() { if (!$this->containsEnumeration()) { throw new InvalidMethodCallException( diff --git a/src/LeanMapper/Reflection/PropertyFactory.php b/src/LeanMapper/Reflection/PropertyFactory.php index ae49144..3daeb23 100644 --- a/src/LeanMapper/Reflection/PropertyFactory.php +++ b/src/LeanMapper/Reflection/PropertyFactory.php @@ -23,6 +23,17 @@ class PropertyFactory { + /** + * Allows to define custom classes or class factories. + * + * Note that each custom class must inherit from the original class + * and each custom factory must return an instance of the original or its descendant. + * + * @var array in format [ original_class_name => extended_class_name_or_factory, ... ] + */ + public static $customClassFactoryMap = []; + + /** * @throws UtilityClassException */ @@ -73,7 +84,7 @@ public static function createFromAnnotation($annotationType, $annotation, Entity ); } - $propertyType = new PropertyType($matches[2], $aliases); + $propertyType = self::createClass('LeanMapper\Reflection\PropertyType', $matches[2], $aliases); $isWritable = $annotationType === 'property'; $containsCollection = $matches[3] !== ''; if ($propertyType->isBasicType() and $containsCollection) { @@ -150,7 +161,7 @@ public static function createFromAnnotation($annotationType, $annotation, Entity "Multiple m:useMethods flags found in property definition: @$annotationType $annotation in entity {$entityReflection->getName()}." ); } - $propertyMethods = new PropertyMethods($name, $isWritable, $flagArgument); + $propertyMethods = self::createClass('LeanMapper\Reflection\PropertyMethods', $name, $isWritable, $flagArgument); break; case 'filter': if ($propertyFilters !== null) { @@ -158,7 +169,7 @@ public static function createFromAnnotation($annotationType, $annotation, Entity "Multiple m:filter flags found in property definition: @$annotationType $annotation in entity {$entityReflection->getName()}." ); } - $propertyFilters = new PropertyFilters($flagArgument); + $propertyFilters = self::createClass('LeanMapper\Reflection\PropertyFilters', $flagArgument); break; case 'passThru': if ($propertyPasses !== null) { @@ -166,7 +177,7 @@ public static function createFromAnnotation($annotationType, $annotation, Entity "Multiple m:passThru flags found in property definition: @$annotationType $annotation in entity {$entityReflection->getName()}." ); } - $propertyPasses = new PropertyPasses($flagArgument); + $propertyPasses = self::createClass('LeanMapper\Reflection\PropertyPasses', $flagArgument); break; case 'enum': if ($propertyValuesEnum !== null) { @@ -179,7 +190,7 @@ public static function createFromAnnotation($annotationType, $annotation, Entity "Parameter of m:enum flag was not found in property definition: @$annotationType $annotation in entity {$entityReflection->getName()}." ); } - $propertyValuesEnum = new PropertyValuesEnum($flagArgument, $entityReflection); + $propertyValuesEnum = self::createClass('LeanMapper\Reflection\PropertyValuesEnum', $flagArgument, $entityReflection); break; case 'column': if ($customColumn !== null) { @@ -255,7 +266,7 @@ public static function createFromAnnotation($annotationType, $annotation, Entity $column = $customColumn ?: $column; - return new Property( + return self::createClass('LeanMapper\Reflection\Property', $name, $entityReflection, $column, @@ -287,7 +298,7 @@ public static function createFromAnnotation($annotationType, $annotation, Entity * @return mixed * @throws InvalidAnnotationException */ - private static function createRelationship( + protected static function createRelationship( $sourceClass, $propertyName, PropertyType $propertyType, @@ -324,7 +335,7 @@ private static function createRelationship( $targetTable, $propertyName ) : self::getSurrogateRelationshipColumn($propertyType)); - return new Relationship\HasOne($pieces[0] ?: $relationshipColumn, $pieces[1] ?: $targetTable); + return self::createClass('LeanMapper\Relationship\HasOne', $pieces[0] ?: $relationshipColumn, $pieces[1] ?: $targetTable); case 'hasMany': $relationshipTable = null; @@ -343,7 +354,7 @@ private static function createRelationship( } } - return new Relationship\HasMany( + return self::createClass('LeanMapper\Relationship\HasMany', $pieces[0] ?: ($mapper !== null ? $mapper->getRelationshipColumn( $mapper->getRelationshipTable($sourceTable, $targetTable), $sourceTable @@ -358,10 +369,10 @@ private static function createRelationship( ); case 'belongsToOne': $relationshipColumn = ($mapper !== null ? $mapper->getRelationshipColumn($targetTable, $sourceTable) : $sourceTable); - return new Relationship\BelongsToOne($pieces[0] ?: $relationshipColumn, $pieces[1] ?: $targetTable, $strategy); + return self::createClass('LeanMapper\Relationship\BelongsToOne', $pieces[0] ?: $relationshipColumn, $pieces[1] ?: $targetTable, $strategy); case 'belongsToMany': $relationshipColumn = ($mapper !== null ? $mapper->getRelationshipColumn($targetTable, $sourceTable) : $sourceTable); - return new Relationship\BelongsToMany($pieces[0] ?: $relationshipColumn, $pieces[1] ?: $targetTable, $strategy); + return self::createClass('LeanMapper\Relationship\BelongsToMany', $pieces[0] ?: $relationshipColumn, $pieces[1] ?: $targetTable, $strategy); } return null; } @@ -372,7 +383,7 @@ private static function createRelationship( * @param string $definition * @return array (definition, flags) */ - private static function parseRelationshipFlags($definition) + protected static function parseRelationshipFlags($definition) { $flags = []; @@ -396,7 +407,7 @@ private static function parseRelationshipFlags($definition) * @param PropertyType $propertyType * @return string */ - private static function getSurrogateRelationshipColumn(PropertyType $propertyType) + protected static function getSurrogateRelationshipColumn(PropertyType $propertyType) { return strtolower(self::trimNamespace($propertyType->getType())) . '{hasOne:' . self::generateRandomString(10) . '}'; } @@ -407,7 +418,7 @@ private static function getSurrogateRelationshipColumn(PropertyType $propertyTyp * @param string $class * @return string */ - private static function trimNamespace($class) + protected static function trimNamespace($class) { $class = explode('\\', $class); return end($class); @@ -419,7 +430,7 @@ private static function trimNamespace($class) * @param int $length * @return string */ - private static function generateRandomString($length) + protected static function generateRandomString($length) { return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length); } @@ -433,7 +444,7 @@ private static function generateRandomString($length) * @return mixed * @throws InvalidAnnotationException */ - private static function fixDefaultValue($value, PropertyType $propertyType, $isNullable) + protected static function fixDefaultValue($value, PropertyType $propertyType, $isNullable) { if ($isNullable and strtolower($value) === 'null') { return null; @@ -472,4 +483,30 @@ private static function fixDefaultValue($value, PropertyType $propertyType, $isN } } + + /** + * Create a class of given name. + * + * Note: this is a variadic function. + * @todo: once support drops to PHP 5.6 and above, rewrite the method to proper variadic function without the need for reflection class + * + * @param string $className + * @param ...$args + * @return mixed + */ + protected static function createClass($className /* , ...$args */) + { + $args = array_slice(func_get_args(), 1); + if (isset(static::$customClassFactoryMap[$className])) { + $customClass = static::$customClassFactoryMap[$className]; + if (is_string($customClass) && class_exists($customClass)) { + $className = $customClass; + } else { + return call_user_func_array($customClass, $args); + } + } + $reflect = new \ReflectionClass($className); + return $reflect->newInstanceArgs($args); + } + } diff --git a/src/LeanMapper/Reflection/PropertyFilters.php b/src/LeanMapper/Reflection/PropertyFilters.php index 56006db..0d23be8 100644 --- a/src/LeanMapper/Reflection/PropertyFilters.php +++ b/src/LeanMapper/Reflection/PropertyFilters.php @@ -22,10 +22,10 @@ class PropertyFilters { /** @var array */ - private $filters = []; + protected $filters = []; /** @var array */ - private $targetedArgs = []; + protected $targetedArgs = []; diff --git a/src/LeanMapper/Reflection/PropertyMethods.php b/src/LeanMapper/Reflection/PropertyMethods.php index f580580..7d6e471 100644 --- a/src/LeanMapper/Reflection/PropertyMethods.php +++ b/src/LeanMapper/Reflection/PropertyMethods.php @@ -22,10 +22,10 @@ class PropertyMethods { /** @var string */ - private $getter; + protected $getter; /** @var string */ - private $setter; + protected $setter; diff --git a/src/LeanMapper/Reflection/PropertyPasses.php b/src/LeanMapper/Reflection/PropertyPasses.php index a76a354..cde2e74 100644 --- a/src/LeanMapper/Reflection/PropertyPasses.php +++ b/src/LeanMapper/Reflection/PropertyPasses.php @@ -22,10 +22,10 @@ class PropertyPasses { /** @var string */ - private $getterPass; + protected $getterPass; /** @var string */ - private $setterPass; + protected $setterPass; diff --git a/src/LeanMapper/Reflection/PropertyType.php b/src/LeanMapper/Reflection/PropertyType.php index 94023b1..fdd706d 100644 --- a/src/LeanMapper/Reflection/PropertyType.php +++ b/src/LeanMapper/Reflection/PropertyType.php @@ -20,10 +20,10 @@ class PropertyType { /** @var string */ - private $type; + protected $type; /** @var bool */ - private $isBasicType; + protected $isBasicType; diff --git a/src/LeanMapper/Reflection/PropertyValuesEnum.php b/src/LeanMapper/Reflection/PropertyValuesEnum.php index 4709845..ff4e6af 100644 --- a/src/LeanMapper/Reflection/PropertyValuesEnum.php +++ b/src/LeanMapper/Reflection/PropertyValuesEnum.php @@ -23,10 +23,10 @@ class PropertyValuesEnum { /** @var array */ - private $values = []; + protected $values = []; /** @var array */ - private $index = []; + protected $index = []; diff --git a/src/LeanMapper/Result.php b/src/LeanMapper/Result.php index 408844b..27dad27 100644 --- a/src/LeanMapper/Result.php +++ b/src/LeanMapper/Result.php @@ -43,43 +43,43 @@ class Result implements \Iterator private static $storedConnection; /** @var bool */ - private $isDetached; + protected $isDetached; /** @var array */ - private $data; + protected $data; /** @var array */ - private $modified = []; + protected $modified = []; /** @var array */ - private $added = []; + protected $added = []; /** @var array */ - private $removed = []; + protected $removed = []; /** @var string */ - private $table; + protected $table; /** @var Connection */ - private $connection; + protected $connection; /** @var IMapper */ protected $mapper; /** @var array */ - private $keys; + protected $keys; /** @var self[] */ - private $referenced = []; + protected $referenced = []; /** @var self[] */ - private $referencing = []; + protected $referencing = []; /** @var array */ - private $index = []; + protected $index = []; /** @var ResultProxy */ - private $proxy; + protected $proxy; @@ -751,7 +751,7 @@ public function rewind() * @param Connection|null $connection * @param IMapper|null $mapper */ - private function __construct(array $data = null, $table = null, Connection $connection = null, IMapper $mapper = null) + protected function __construct(array $data = null, $table = null, Connection $connection = null, IMapper $mapper = null) { $this->data = $data !== null ? $data : [self::DETACHED_ROW_ID => []]; $this->table = $table; @@ -770,7 +770,7 @@ private function __construct(array $data = null, $table = null, Connection $conn * @throws InvalidStateException * @return self */ - private function getReferencedResult($table, $viaColumn, Filtering $filtering = null) + protected function getReferencedResult($table, $viaColumn, Filtering $filtering = null) { if ($this->isDetached) { throw new InvalidStateException('Cannot get referenced Result for detached Result.'); @@ -843,7 +843,7 @@ private function getReferencedResult($table, $viaColumn, Filtering $filtering = * @throws InvalidStateException * @return self */ - private function getReferencingResult($table, $viaColumn = null, Filtering $filtering = null, $strategy = self::STRATEGY_IN) + protected function getReferencingResult($table, $viaColumn = null, Filtering $filtering = null, $strategy = self::STRATEGY_IN) { $strategy = $this->translateStrategy($strategy); if ($this->isDetached) { @@ -958,7 +958,7 @@ private function getReferencingResult($table, $viaColumn = null, Filtering $filt * @param string $column * @return array */ - private function extractIds($column) + protected function extractIds($column) { if ($this->isAlias($column)) { $column = $this->trimAlias($column); @@ -982,7 +982,7 @@ private function extractIds($column) * @param Filtering|null $filtering * @return mixed */ - private function buildUnionStrategySql(array $ids, $table, $viaColumn, Filtering $filtering = null) + protected function buildUnionStrategySql(array $ids, $table, $viaColumn, Filtering $filtering = null) { $isAlias = $this->isAlias($viaColumn); if ($isAlias) { @@ -1015,7 +1015,7 @@ private function buildUnionStrategySql(array $ids, $table, $viaColumn, Filtering * @param array $relatedKeys * @return Fluent */ - private function createTableSelection($table, $relatedKeys = null) + protected function createTableSelection($table, $relatedKeys = null) { $selection = $this->connection->select('%n.*', $table)->from('%n', $table); return $relatedKeys !== null ? $selection->setRelatedKeys($relatedKeys) : $selection; @@ -1028,7 +1028,7 @@ private function createTableSelection($table, $relatedKeys = null) * @throws InvalidArgumentException * @return string */ - private function translateStrategy($strategy) + protected function translateStrategy($strategy) { if ($strategy === null) { $strategy = self::STRATEGY_IN; @@ -1048,7 +1048,7 @@ private function translateStrategy($strategy) * @return FilteringResultDecorator|null * @throws InvalidArgumentException */ - private function applyFiltering(Fluent $statement, Filtering $filtering) + protected function applyFiltering(Fluent $statement, Filtering $filtering) { $targetedArgs = $filtering->getTargetedArgs(); foreach ($filtering->getFilters() as $filter) { @@ -1078,7 +1078,7 @@ private function applyFiltering(Fluent $statement, Filtering $filtering) * @param array $arguments * @return string */ - private function calculateArgumentsHash(array $arguments) + protected function calculateArgumentsHash(array $arguments) { return md5(serialize($arguments)); } @@ -1089,7 +1089,7 @@ private function calculateArgumentsHash(array $arguments) * @param string $column * @return bool */ - private function isAlias($column) + protected function isAlias($column) { return strncmp($column, '*', 1) === 0; } @@ -1100,7 +1100,7 @@ private function isAlias($column) * @param string $column * @return string */ - private function trimAlias($column) + protected function trimAlias($column) { return substr($column, 1); }