Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @Virtual annotation #109

Merged
merged 9 commits into from
Jun 8, 2012
2 changes: 1 addition & 1 deletion Annotation/Groups.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

/**
* @Annotation
* @Target("PROPERTY")
* @Target({"PROPERTY","METHOD"})
*/
final class Groups
{
Expand Down
2 changes: 1 addition & 1 deletion Annotation/Inline.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

/**
* @Annotation
* @Target({"PROPERTY"})
* @Target({"PROPERTY","METHOD"})
*/
final class Inline
{
Expand Down
2 changes: 1 addition & 1 deletion Annotation/SerializedName.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

/**
* @Annotation
* @Target("PROPERTY")
* @Target({"PROPERTY","METHOD"})
*/
final class SerializedName
{
Expand Down
2 changes: 1 addition & 1 deletion Annotation/Since.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

/**
* @Annotation
* @Target("PROPERTY")
* @Target({"PROPERTY", "METHOD"})
*/
final class Since extends Version
{
Expand Down
2 changes: 1 addition & 1 deletion Annotation/Until.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

/**
* @Annotation
* @Target("PROPERTY")
* @Target({"PROPERTY", "METHOD"})
*/
final class Until extends Version
{
Expand Down
13 changes: 13 additions & 0 deletions Annotation/VirtualProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace JMS\SerializerBundle\Annotation;

/**
* @Annotation
* @Target("METHOD")
*
* @author Alexander Klimenkov <alx.devel@gmail.com>
*/
final class VirtualProperty
{
}
2 changes: 1 addition & 1 deletion Annotation/XmlAttribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

/**
* @Annotation
* @Target("PROPERTY")
* @Target({"PROPERTY", "METHOD"})
*/
final class XmlAttribute
{
Expand Down
2 changes: 1 addition & 1 deletion Annotation/XmlList.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

/**
* @Annotation
* @Target("PROPERTY")
* @Target({"PROPERTY","METHOD"})
*/
final class XmlList extends XmlCollection
{
Expand Down
2 changes: 1 addition & 1 deletion Annotation/XmlMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

/**
* @Annotation
* @Target("PROPERTY")
* @Target({"PROPERTY","METHOD"})
*/
final class XmlMap extends XmlCollection
{
Expand Down
2 changes: 1 addition & 1 deletion Annotation/XmlValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

/**
* @Annotation
* @Target("PROPERTY")
* @Target({"PROPERTY","METHOD"})
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you reviewed all the other annotations? Whether they make sense on methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now supported annotations Groups, SerializedName, Version, XmlAttribute, XmlList, Inline, Since, Until, VirtualProperty, XmlMap, XmlValue. For other annotations it's don't need

*/
final class XmlValue
{
Expand Down
44 changes: 22 additions & 22 deletions Metadata/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,32 +138,32 @@ public function unserialize($str)

private function sortProperties()
{
switch ($this->accessorOrder) {
case self::ACCESSOR_ORDER_ALPHABETICAL:
ksort($this->propertyMetadata);
break;
case self::ACCESSOR_ORDER_CUSTOM:
switch ($this->accessorOrder) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What has been changed here? whitespace?

case self::ACCESSOR_ORDER_ALPHABETICAL:
ksort($this->propertyMetadata);
break;

case self::ACCESSOR_ORDER_CUSTOM:
$order = $this->customOrder;
uksort($this->propertyMetadata, function($a, $b) use ($order) {
$existsA = isset($order[$a]);
$existsB = isset($order[$b]);

if (!$existsA && !$existsB) {
return 0;
}

if (!$existsA) {
return 1;
}

if (!$existsB) {
return -1;
$existsA = isset($order[$a]);
$existsB = isset($order[$b]);

if (!$existsA && !$existsB) {
return 0;
}

if (!$existsA) {
return 1;
}

return $order[$a] < $order[$b] ? -1 : 1;
if (!$existsB) {
return -1;
}

return $order[$a] < $order[$b] ? -1 : 1;
});
break;
}
break;
}
}
}
61 changes: 40 additions & 21 deletions Metadata/Driver/AnnotationDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
use JMS\SerializerBundle\Annotation\PostSerialize;
use JMS\SerializerBundle\Annotation\PostDeserialize;
use JMS\SerializerBundle\Annotation\PreSerialize;
use JMS\SerializerBundle\Annotation\VirtualProperty;
use Metadata\MethodMetadata;
use Doctrine\Common\Annotations\Reader;
use JMS\SerializerBundle\Annotation\Type;
Expand All @@ -46,6 +47,7 @@
use JMS\SerializerBundle\Annotation\ReadOnly;
use JMS\SerializerBundle\Metadata\ClassMetadata;
use JMS\SerializerBundle\Metadata\PropertyMetadata;
use JMS\SerializerBundle\Metadata\VirtualPropertyMetadata;
use Metadata\Driver\DriverInterface;

class AnnotationDriver implements DriverInterface
Expand All @@ -62,6 +64,9 @@ public function loadMetadataForClass(\ReflectionClass $class)
$classMetadata = new ClassMetadata($name = $class->getName());
$classMetadata->fileResources[] = $class->getFilename();

$propertiesMetadata = array();
$propertiesAnnotations = array();

$exclusionPolicy = 'NONE';
$excludeAll = false;
$classAccessType = PropertyMetadata::ACCESS_TYPE_PROPERTY;
Expand All @@ -79,17 +84,50 @@ public function loadMetadataForClass(\ReflectionClass $class)
}
}

foreach ($class->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() !== $name) {
continue;
}

$methodAnnotations = $this->reader->getMethodAnnotations($method);

foreach ($methodAnnotations as $annot) {
if ($annot instanceof PreSerialize) {
$classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->getName()));
continue 2;
} else if ($annot instanceof PostDeserialize) {
$classMetadata->addPostDeserializeMethod(new MethodMetadata($name, $method->getName()));
continue 2;
} else if ($annot instanceof PostSerialize) {
$classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->getName()));
continue 2;
} else if ($annot instanceof VirtualProperty) {
$virtualPropertyMetadata = new VirtualPropertyMetadata($name, $method->getName());
$propertiesMetadata[] = $virtualPropertyMetadata;
$propertiesAnnotations[] = $methodAnnotations;
continue 2;
}
}
}

if (!$excludeAll) {
foreach ($class->getProperties() as $property) {
if ($property->getDeclaringClass()->getName() !== $name) {
continue;
}
$propertiesMetadata[] = new PropertyMetadata($name, $property->getName());
$propertiesAnnotations[] = $this->reader->getPropertyAnnotations($property);
}

foreach ($propertiesMetadata as $propertyKey => $propertyMetadata) {

$propertyMetadata = new PropertyMetadata($name, $property->getName());
$isExclude = $isExpose = false;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$isExclude and $isExpose only apply to the real properties, we should probably move them into the loop above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't sure, that this a good idea. If we do this, we will must to split loop over $propertyAnnotations on two parts: first for "real" properties and second - for virtual. IMHO, it's bad.
By the way, (I know, it's implicitly, but) methods can not to have "Exclude", "Expose" and "Accessor" annotations.
Tell me, please, if i missed something.

$AccessType = $classAccessType;
$accessor = array(null, null);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for $accessor.

foreach ($this->reader->getPropertyAnnotations($property) as $annot) {

$propertyAnnotations = $propertiesAnnotations[$propertyKey];

foreach ($propertyAnnotations as $annot) {
if ($annot instanceof Since) {
$propertyMetadata->sinceVersion = $annot->version;
} else if ($annot instanceof Until) {
Expand Down Expand Up @@ -137,25 +175,6 @@ public function loadMetadataForClass(\ReflectionClass $class)
}
}

foreach ($class->getMethods() as $method) {
if ($method->getDeclaringClass()->getName() !== $name) {
continue;
}

foreach ($this->reader->getMethodAnnotations($method) as $annot) {
if ($annot instanceof PreSerialize) {
$classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->getName()));
continue 2;
} else if ($annot instanceof PostDeserialize) {
$classMetadata->addPostDeserializeMethod(new MethodMetadata($name, $method->getName()));
continue 2;
} else if ($annot instanceof PostSerialize) {
$classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->getName()));
continue 2;
}
}
}

return $classMetadata;
}
}
31 changes: 26 additions & 5 deletions Metadata/Driver/XmlDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use JMS\SerializerBundle\Exception\XmlErrorException;
use JMS\SerializerBundle\Annotation\ExclusionPolicy;
use JMS\SerializerBundle\Metadata\PropertyMetadata;
use JMS\SerializerBundle\Metadata\VirtualPropertyMetadata;
use Metadata\MethodMetadata;
use JMS\SerializerBundle\Metadata\ClassMetadata;
use Metadata\Driver\AbstractFileDriver;
Expand Down Expand Up @@ -50,6 +51,9 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $path)
$excludeAll = null !== ($exclude = $elem->attributes()->exclude) ? 'true' === strtolower($exclude) : false;
$classAccessType = (string) ($elem->attributes()->{'access-type'} ?: PropertyMetadata::ACCESS_TYPE_PROPERTY);

$propertiesMetadata = array();
$propertiesNodes = array();

if (null !== $accessorOrder = $elem->attributes()->{'accessor-order'}) {
$metadata->setAccessorOrder((string) $accessorOrder, preg_split('/\s*,\s*/', (string) $elem->attributes()->{'custom-accessor-order'}));
}
Expand All @@ -58,19 +62,36 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $path)
$metadata->xmlRootName = (string) $xmlRootName;
}

foreach ($elem->xpath('./virtual-property') as $method) {
if (!isset($method->attributes()->method)) {
throw new RuntimeException('The method attribute must be set for all virtual-property elements.');
}

$virtualPropertyMetadata = new VirtualPropertyMetadata( $name, (string) $method->attributes()->method );

$propertiesMetadata[] = $virtualPropertyMetadata;
$propertiesNodes[] = $method;
}

if (!$excludeAll) {

foreach ($class->getProperties() as $property) {
if ($name !== $property->getDeclaringClass()->getName()) {
continue;
}

$propertiesMetadata[] = new PropertyMetadata($name, $pName = $property->getName());
$pElems = $elem->xpath("./property[@name = '".$pName."']");

$propertiesNodes[] = $pElems ? reset( $pElems ) : null;
}

foreach ($propertiesMetadata as $propertyKey => $pMetadata) {

$pMetadata = new PropertyMetadata($name, $pName = $property->getName());
$isExclude = $isExpose = false;

$pElems = $elem->xpath("./property[@name = '".$pName."']");

if ($pElems) {
$pElem = reset($pElems);
$pElem = $propertiesNodes[$propertyKey];
if (!empty( $pElem )) {

if (null !== $exclude = $pElem->attributes()->exclude) {
$isExclude = 'true' === strtolower($exclude);
Expand Down
24 changes: 23 additions & 1 deletion Metadata/Driver/YamlDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use JMS\SerializerBundle\Annotation\ExclusionPolicy;
use Metadata\MethodMetadata;
use JMS\SerializerBundle\Metadata\PropertyMetadata;
use JMS\SerializerBundle\Metadata\VirtualPropertyMetadata;
use JMS\SerializerBundle\Metadata\ClassMetadata;
use Symfony\Component\Yaml\Yaml;
use Metadata\Driver\AbstractFileDriver;
Expand All @@ -44,6 +45,8 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $file)
$excludeAll = isset($config['exclude']) ? (Boolean) $config['exclude'] : false;
$classAccessType = isset($config['access_type']) ? $config['access_type'] : PropertyMetadata::ACCESS_TYPE_PROPERTY;

$propertiesMetadata = array();

if (isset($config['accessor_order'])) {
$metadata->setAccessorOrder($config['accessor_order'], isset($config['custom_accessor_order']) ? $config['custom_accessor_order'] : array());
}
Expand All @@ -52,13 +55,32 @@ protected function loadMetadataFromFile(\ReflectionClass $class, $file)
$metadata->xmlRootName = (string) $config['xml_root_name'];
}

if (array_key_exists('virtual_properties', $config) ) {

foreach ( $config['virtual_properties'] as $methodName => $propertySettings ) {

if ( !$class->hasMethod( $methodName ) ) {
throw new RuntimeException('The method '.$methodName.' not found in class ' . $class->name);
}

$virtualPropertyMetadata = new VirtualPropertyMetadata( $name, $methodName );

$propertiesMetadata[$methodName] = $virtualPropertyMetadata;
$config['properties'][$methodName] = $propertySettings;
}
}

if (!$excludeAll) {
foreach ($class->getProperties() as $property) {
if ($name !== $property->getDeclaringClass()->getName()) {
continue;
}
$pName = $property->getName();
$propertiesMetadata[$pName] = new PropertyMetadata($name, $pName);
}

foreach ($propertiesMetadata as $pName => $pMetadata) {

$pMetadata = new PropertyMetadata($name, $pName = $property->getName());
$isExclude = $isExpose = false;
if (isset($config['properties'][$pName])) {
$pConfig = $config['properties'][$pName];
Expand Down