Skip to content

Commit

Permalink
Moved code around and small fixes
Browse files Browse the repository at this point in the history
1. moved information on properties outside of the HydratorMethodsVisitor into the HydratorGenerator
2. moved exception code into it's own class InvalidOptionException
3. moved option parsing from HydratorMethodsVisitor to value object AllowedPropertiesOption
4. small fix where a null would not be hydrated (in case the object itself has another default value)
  • Loading branch information
flip111 committed Nov 23, 2015
1 parent a5ff50c commit 675ab1e
Show file tree
Hide file tree
Showing 5 changed files with 314 additions and 154 deletions.
155 changes: 155 additions & 0 deletions src/GeneratedHydrator/ClassGenerator/AllowedPropertiesOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license.
*/

namespace GeneratedHydrator\ClassGenerator;

use GeneratedHydrator\Exception\InvalidOptionException;
use ReflectionClass;

/**
* Generator for highly performing {@see \Zend\Stdlib\Hydrator\HydratorInterface}
* for objects
*
* {@inheritDoc}
*
* @author Marco Pivetta <ocramius@gmail.com>
* @license MIT
*/
class AllowedPropertiesOption {
/**
* When this option is passed, only the properties in the given array are
* hydrated and extracted.
*/
const OPTION_ALLOWED_PROPERTIES = 'allowedProperties';

/**
* @var PropertyAccessor[]
*/
private $propertyNames;

/**
* @var array Holds configuration for the object properties.
*/
private $allowedProperties;

public function __construct(ReflectionClass $reflectedClass, array $options) {
$this->propertyNames = array_map(function($prop) {
return $prop->name;
}, $reflectedClass->getProperties());

$this->allowedProperties = $this->expandAllowedProperties($options);
}

/**
* Returns an array with properties as keys and hydrate/extract information
* as values.
*
* @param array $options
*/
private function expandAllowedProperties($options)
{
$allowedProperties = [];

// Option was not given
if (! isset($options[static::OPTION_ALLOWED_PROPERTIES])) {
foreach ($this->propertyNames as $propertyName) {
$allowedProperties[$propertyName] = [
'extract' => true,
'hydrate' => true
];
}

return $allowedProperties;
}

if (! is_array($options[static::OPTION_ALLOWED_PROPERTIES])) {
throw InvalidOptionException::valueNotArray(gettype($options[static::OPTION_ALLOWED_PROPERTIES]));
}

// Option was given
foreach ($options[static::OPTION_ALLOWED_PROPERTIES] as $k => $v) {
// simple format
if (is_int($k)) {
$this->makeSimpleFormat($k, $v, $allowedProperties);

continue;
}

// advanced format
if (is_string($k)) {
$this->makeAdvancedFormat($k, $v, $allowedProperties);
}
}

// Disable all properties which are not specified in the allowedProperties
foreach ($this->propertyNames as $propertyName) {
if (! in_array($propertyName, array_keys($allowedProperties))) {
$allowedProperties[$propertyName] = [
'extract' => false,
'hydrate' => false
];
}
}

return $allowedProperties;
}

private function makeSimpleFormat($key, $value, &$allowedProperties) {
if (! is_string($value)) {
throw InvalidOptionException::invalidValueExpectedString(gettype($value), $key);
}

if (in_array($value, array_keys($allowedProperties))) {
throw InvalidOptionException::doubleProperty($value);
}

$allowedProperties[$value] = [
'extract' => true,
'hydrate' => true
];
}

private function makeAdvancedFormat($key, $value, &$allowedProperties) {
if (! is_array($value)) {
throw InvalidOptionException::arrayExpected($k, gettype($value));
}

if (in_array($key, $allowedProperties)) {
throw InvalidOptionException::doubleProperty($value);
}

$validateOptionConfigurationKey = function($property, $array, $key) {
if (! isset($array[$key])) {
throw InvalidOptionException::missingKey($property, $key);
}

if (! in_array($array[$key], [true, false, 'optional'])) {
throw InvalidOptionException::invalidValue($property, $key);
}
};

$validateOptionConfigurationKey($key, $value, 'extract');
$validateOptionConfigurationKey($key, $value, 'hydrate');

$allowedProperties[$key] = $value;
}

public function getAllowedProperties() {
return $this->allowedProperties;
}
}
53 changes: 52 additions & 1 deletion src/GeneratedHydrator/ClassGenerator/HydratorGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
use CodeGenerationUtils\Visitor\ClassExtensionVisitor;
use CodeGenerationUtils\Visitor\ClassImplementorVisitor;
use CodeGenerationUtils\Visitor\MethodDisablerVisitor;
use GeneratedHydrator\ClassGenerator\Hydrator\PropertyGenerator\PropertyAccessor;
use GeneratedHydrator\CodeGenerator\Visitor\HydratorMethodsVisitor;
use PhpParser\NodeTraverser;
use ReflectionClass;
use ReflectionProperty;

/**
* Generator for highly performing {@see \Zend\Stdlib\Hydrator\HydratorInterface}
Expand Down Expand Up @@ -67,12 +69,61 @@ function () {
// step 2: implement new methods and interfaces, extend original class
$implementor = new NodeTraverser();

$implementor->addVisitor(new HydratorMethodsVisitor($originalClass, $options));
// prepare information which can be used by visitors
$accessibleProperties = $this->getAccessibleProperties($originalClass);
$propertyWriters = $this->getPropertyWriters($originalClass);
$allowedPropertiesOption = new AllowedPropertiesOption($originalClass, $options);

$implementor->addVisitor(new HydratorMethodsVisitor($accessibleProperties, $propertyWriters, $allowedPropertiesOption));
$implementor->addVisitor(new ClassExtensionVisitor($originalClass->getName(), $originalClass->getName()));
$implementor->addVisitor(
new ClassImplementorVisitor($originalClass->getName(), array('Zend\\Stdlib\\Hydrator\\HydratorInterface'))
);

return $implementor->traverse($ast);
}

/**
* Retrieve instance public/protected properties
*
* @param ReflectionClass $reflectedClass
*
* @return ReflectionProperty[]
*/
private function getAccessibleProperties(ReflectionClass $reflectedClass)
{
return array_filter(
$reflectedClass->getProperties(),
function (ReflectionProperty $property) {
return ($property->isPublic() || $property->isProtected()) && ! $property->isStatic();
}
);
}

/**
* Retrieve instance private properties
*
* @param ReflectionClass $reflectedClass
*
* @return ReflectionProperty[]
*/
private function getPrivateProperties(ReflectionClass $reflectedClass)
{
return array_filter(
$reflectedClass->getProperties(),
function (ReflectionProperty $property) {
return $property->isPrivate() && ! $property->isStatic();
}
);
}

private function getPropertyWriters(ReflectionClass $reflectedClass) {
$propertyWriters = [];

foreach ($this->getPrivateProperties($reflectedClass) as $property) {
$propertyWriters[$property->getName()] = new PropertyAccessor($property, 'Writer');
}

return $propertyWriters;
}
}
Loading

0 comments on commit 675ab1e

Please sign in to comment.