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/Exclude.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

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

Choose a reason for hiding this comment

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

I know I suggested to allow this on methods, but I'm not so sure anymore.

Basically, we always exclude them unless you have set a @VirtualProperty annotation. So, adding @VirtualProperty, and then @exclude does not seem to make sense to me. So, I'd say keep this annotation as is.

*/
final class Exclude
{
Expand Down
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/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
18 changes: 18 additions & 0 deletions Annotation/VirtualProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace JMS\SerializerBundle\Annotation;

/**
* @Annotation
* @Target("METHOD")
*
* @author Alexander Klimenkov <alx.devel@gmail.com>
*/
final class VirtualProperty
{
/**
* @Required
* @var string
*/
public $field;
Copy link
Owner

Choose a reason for hiding this comment

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

What about removing this? It's redundant with @SerializedName.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but if we remove this, VirtualProperty annotation will work only together with SerializedName. If you sure, that this variant is better, i can try remove this.

Copy link
Owner

Choose a reason for hiding this comment

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

I think we could use some smart default based on the method name.

if ('get' === substr($method, 0, 3)) {
    $name = lcfirst(substr($method, 3));
} else {
    $name = $method;
}

}
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/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;
}
}
}
62 changes: 41 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,51 @@ 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, $annot->field);
$virtualPropertyMetadata->getter = $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 +176,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;
}
}
92 changes: 92 additions & 0 deletions Metadata/VirtualPropertyMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace JMS\SerializerBundle\Metadata;


class VirtualPropertyMetadata extends PropertyMetadata
{

public function __construct($class, $name)
{
$this->class = $class;
$this->name = $name;
$this->readOnly = true;
}

public function getValue($obj)
{
return $obj->{$this->getter}();
}

public function setValue($obj, $value)
{
return false;
}

public function setAccessor($type, $getter = null, $setter = null)
{
return false;
}

public function serialize()
{
return serialize(array(
$this->sinceVersion,
$this->untilVersion,
$this->groups,
$this->serializedName,
$this->type,
$this->xmlCollection,
$this->xmlCollectionInline,
$this->xmlEntryName,
$this->xmlKeyAttribute,
$this->xmlAttribute,
$this->xmlValue,
$this->getter,
$this->setter,
$this->inline,
$this->readOnly,
$this->class,
$this->name
));
}

public function unserialize($str)
{
list(
$this->sinceVersion,
$this->untilVersion,
$this->groups,
$this->serializedName,
$this->type,
$this->xmlCollection,
$this->xmlCollectionInline,
$this->xmlEntryName,
$this->xmlKeyAttribute,
$this->xmlAttribute,
$this->xmlValue,
$this->getter,
$this->setter,
$this->inline,
$this->readOnly,
$this->class,
$this->name
) = unserialize($str);
}
}
64 changes: 64 additions & 0 deletions Tests/Fixtures/ObjectWithVirtualProperties.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

/*
* Copyright 2011 Johannes M. Schmitt <schmittjoh@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace JMS\SerializerBundle\Tests\Fixtures;

use JMS\SerializerBundle\Annotation\AccessorOrder;
use JMS\SerializerBundle\Annotation\Type;
use JMS\SerializerBundle\Annotation\VirtualProperty;
use JMS\SerializerBundle\Annotation\SerializedName;
use JMS\SerializerBundle\Annotation\Exclude;

/**
* @AccessorOrder("custom", custom = {"prop_name", "existField", "foo" })
*/
class ObjectWithVirtualProperties
{

/**
* @Type("string")
*/
protected $existField = 'value';

/**
*
* @VirtualProperty("foo")
*/
public function getVirualValue()
{
return 'value';
}

/**
* @VirtualProperty("prop_name")
* @SerializedName("test")
*/
public function getVirualSerializedValue()
{
return 'other-name';
}

/**
* @VirtualProperty("excluded")
* @Exclude
*/
public function getVirualExcudedValue()
{
return 'excluded';
}
}